# C/C++ 語法 ###### tags: `面試準備` ## new/delete/malloc/free (todo) 實際看 new source code 看分配 behavior - new/malloc 和 delete/free 最大差別在於會不會呼叫 constructor/destructor , 因為 new/delete 實做過程中包含 malloc/free 只是多加了其他步驟 - 使用 example: ```cpp // malloc int *p = (int*)malloc(sizeof(int)); //free free(p); //new int *p_n = new int; // delete delete p_n; ``` - 我們看看初始化的行為 ```cpp class foo { public: foo() { printf("Constructor of foo().\n"); } ~foo() { printf("Destructor of foo().\n"); } }; cout<<"malloc:"<<endl; foo *foo_p = (foo*)malloc(sizeof(foo)); free(foo_p); cout<<endl<<"new:"<<endl; foo *foo_p_n = new foo(); delete foo_p_n; ``` - output ```shell malloc: new: Constructor of foo(). Destructor of foo(). ``` - 可以發現 new/delete 會呼叫 constructor/desturctor , malloc/free 則不會,單純申請/釋放記憶體。 ## extern/static extern 和 static 因為描述的性質某種程度上相反,很常拿來放在一起比較。 ### extern 當我們在不同的 file 裡面想要使用共同的變數,可以使用 extern 完成,如以下範例 - extern.h ```cpp // global variable int foo = 100; ``` - main.cpp ```cpp #include"extern.h" int main(void) { extern int foo; printf("%d\n", foo); } ``` - output ``` 100 ``` C/C++ 可支援多次宣告, `extern int foo` 就是第二次宣告,並告訴 compile 這個 foo 有宣告過進而照到 extern.h 中的 global variable ### static > Reference: [link](https://kamory0931.pixnet.net/blog/post/119201381) static 的語法就相對複雜,他希望達到的目的正好相反,第一個作用是限定作用域;第二個作用是保持變數內容持久化。 #### static variable 他的生命週期跟著 scope ,所以即使他宣告於 function 之中依然不會被消滅,如以下的例子 ```cpp #include<iostream> using namespace std; void TestFunction(int i, int nEnd) { static int x = 0; int y = 0; x++; y++; cout<<"x="<<x<<", y="<<y<<endl; } int main() { for(int i = 0 ; i < 5 ; i++) TestFunction(i, 5); } ``` - output ``` x=1, y=1 x=2, y=1 x=3, y=1 x=4, y=1 x=5, y=1 ``` - 發現 x 沒有被消滅,函數中的變數 y 則在函式結束就被消滅了。 #### static member variable 指的是 class 中的 static 變數,這個變數特別的地方在於他不需要 class object 被初始化便能使用,是該 class 專屬變數。 ```cpp class A { public: //宣告static member variable static int m_nNum; }; //必須定義在外面,不寫不行 //int A::m_nNum 也行 int A::m_nNum = 0; int main(){ //如果m_nNum是private的,那就不能用這種方式取值了 cout<<A::m_nNum<<endl; } ``` #### static member function static member function 也跟 static member variable 一樣,獨立於物件存在。可以在沒有物件產生的狀況下被使用,所以他不能使用 this,也不能存取 class 裡的一般成員變數,當然也不能使用一般成員函式了。也就是說,static member function 只能存取 static member variable。 ```cpp class A { private: //宣告static member variable static int m_nNum; public: int m_nNum2; int m_nNum3; public: static int GetNum() {return m_nNum;} static int GetNum2() {return m_nNum2;} //編譯不會過 static int StaticGetNum3() {return GetNum3();} //編譯不會過 int GetNum3() {return m_nNum3;} A() {m_nNum2 = 10; m_nNum3=20;} }; ``` :::success 用途?(todo) ::: #### static global function/variable 最常用的方式,目的是避免重複命名。 ```c static int val = 0; ``` 這個變數的含義是該cpp內有效,但是其他的cpp檔案不能訪問這個變數;如果有兩個cpp檔案聲明瞭同名的全域性靜態變數,那麼他們實際上是獨立的兩個變數 ## inline function > reference: [link](http://www.slmt.tw/google-cpp-style-guide-zh-tw/header-files/inline-functions.html) 連結內寫得很詳細,主要概念是如果你的 function 長度沒有很長, inline 會**建議** compiler 把 function 直接展開而不是用呼叫的模式 - 可想而知過長的 function 或 recursion 並不適合 延伸閱讀: [inline variable](https://zh-blog.logan.tw/2020/03/22/cxx-17-inline-variable/#id2) ## Lambda expression > reference: [link](https://blog.gtwang.org/programming/lambda-expression-in-c11/) lambda expression 是 c++11 才出現的語法,lambda expression 是一種匿名函數的表示方式,它可以讓程式設計師將函數的內容直接以 inline 的方式寫在一般的程式碼之中 - example ```cpp #include <algorithm> #include <cmath> void abssort(float* x, unsigned n) { std::sort(x, x + n, // Lambda expression begins [](float a, float b) { return (std::abs(a) < std::abs(b)); } // end of lambda expression ); } ``` ![](https://i.imgur.com/3RdmF8K.png) 1. capture 子句(也稱為 c + + 規格中的 lambda introducer) 2. 參數清單 (也稱為 lambda 宣告子) 3. 可變規格 4. 例外狀況規格 5. 尾端-傳回類型 6. lambda 主體 ## Template > reference: [link1](https://openhome.cc/Gossip/CppGossip/FunctionTemplate.html) , [link2](https://docs.microsoft.com/zh-tw/cpp/cpp/templates-cpp?view=msvc-160) template 是讓 compiler 自動判斷變數型態來填入的方式 ```cpp #include <iostream> using namespace std; template <typename T> bool greaterThan(T a, T b) { return a > b; } int main() { // 顯示 0 cout << greaterThan(10, 20) << endl; // 顯示 1 cout << greaterThan(string("xyz"), string("abc")) << endl; return 0; } ``` ## Static_cast > Reference: [link](https://docs.microsoft.com/zh-tw/cpp/cpp/static-cast-operator?view=msvc-160) 簡單來說就是做型態轉換 > Usage: [4種用法](https://e8859487.pixnet.net/blog/post/402001658-%5Bc%2B%2B%5D-static_cast-%E7%94%A8%E6%B3%95%E8%AA%AA%E6%98%8E---%28%E5%9F%BA%E7%A4%8E%E7%AF%87%29), [C style](https://shininglionking.blogspot.com/2018/05/c-staticcast-reinterpretcast-c-style.html) ## ++ - ++ prefix 會 call 的函數 ```cpp ++d1; d1.operator++() Date &operator++() ``` - ++ postfix 會 call 的函數 ```cpp d1++; d1.operator++(0) Date operator++(int) ``` Example - prefix ```cpp int i=3, j=0; j=++i; //此時i=4,j=4 ``` - postfix ```cpp int i=3, j=0; j=++i; //此時i=4,j=3 ``` 實際建構 ++ prefix / postfix 例子 ```cpp class Test { public: int m_i; // prefix Test & operator++() { ++(this->m_i); return *this; } const Test operator++(int) { Test t = *this; ++(*this); return t; } }; ``` ## GeekForGeeks - Template - Q1 ```cpp #include <iostream> using namespace std; template <typename T> void fun(const T&x) { static int count = 0; cout << "x = " << x << " count = " << count << endl; ++count; return; } int main() { fun<int> (1); cout << endl; fun<int>(1); cout << endl; fun<double>(1.1); cout << endl; return 0; } ``` Output ``` x = 1 count = 0 x = 1 count = 1 x = 1.1 count = 0 ``` :::success Compiler creates a new instance of a template function for every data type. So compiler creates two functions in the above example, one for int and other for double. Every instance has its own copy of static variable. The int instance of function is called twice, so count is incremented for the second call. ::: - Q2 ```cpp #include<iostream> #include<stdlib.h> using namespace std; template<class T, class U> class A { T x; U y; static int count; }; int main() { A<char, char> a; A<int, int> b; cout << sizeof(a) << endl; cout << sizeof(b) << endl; return 0; } ``` Output ``` 2 8 ``` :::success Since count is static, it is not counted in sizeof. ::: - Q3 ```cpp #include <iostream> using namespace std; template <class T, int max> int arrMin(T arr[], int n) { int m = max; for (int i = 0; i < n; i++) if (arr[i] < m) m = arr[i]; return m; } int main() { int arr1[] = {10, 20, 15, 12}; int n1 = sizeof(arr1)/sizeof(arr1[0]); char arr2[] = {1, 2, 3}; int n2 = sizeof(arr2)/sizeof(arr2[0]); cout << arrMin<int, 10000>(arr1, n1) << endl; cout << arrMin<char, 256>(arr2, n2); return 0; } ``` Output ``` 10 1 ``` :::success We can pass non-type arguments to templates. Non-type parameters are mainly used for specifying max or min values or any other constant value for a particular instance of template. The important thing to note about non-type parameters is, they must be const. Compiler must know the value of non-type parameters at compile time. Because compiler needs to create functions/classes for a specified non-type value at compile time. Following is another example of non-type parameters. ```cpp #include <iostream> using namespace std; template < class T, int N > T fun (T arr[], int size) { if (size > N) cout << "Not possible"; T max = arr[0]; for (int i = 1; i < size; i++) if (max < arr[i]) max = arr[i]; return max; } int main () { int arr[] = {12, 3, 14}; cout << fun (arr, 3); } ``` ::: ## typedef vs. #define in C - #define 是字串替代,幾乎沒有限制 - typedef 必須是原先就有的變數型態 - `typedef unsigned char BYTE` 差別在於,如果你一次宣告多個變數: ``` #definde int* INT_PTR INT_PTR a,b,c; ``` 會變成 ``` int *a, b, c; ``` 如果是用 typedef 就能符合預期 ``` typedef int* INT_PTR INT_PTR a,b,c; ``` 結果會是 ``` int *a, *b, *c; ``` - typdef 還有一個特別的功能 - ```typdef char (*fun)()``` 代表定義 fun pointer 回傳 char ### Pointer - Q1 ```c #include <stdio.h> int main() { int a[5] = {1,2,3,4,5}; int *ptr = (int*)(&a+1); printf("%d %d", *(a+1), *(ptr-1)); return 0; } ``` Output ``` 2 5 ``` :::success - The program prints “2 5″. - Since compilers convert array operations in pointers before accessing the array elements, (a+1) points to 2. - The expression (&a + 1) is actually an address just after end of array ( after address of 5 ) because &a contains address of an item of size 5*integer_size and when we do (&a + 1) the pointer is incremented by 5*integer_size. - ptr is type-casted to int * so when we do ptr -1, we get address of 5 :::