# C/C++ ###### tags: `programming` `C/C++` 編譯ctrl+shift+b 執行ctrl+alt+n [其他C++參考文獻](https://hackmd.io/@learnc/S18oUSBxK?utm_source=preview-mode&utm_medium=rec#C) ## 為何 c++ 不建議使用 malloc (動態配置記憶體空間) > 在 C++ 中,malloc 函数和 free 函数是用于分配和释放内存的 C 标准库函数。在 C++ 中,更推荐使用 new 和 delete 运算符来进行内存的动态分配和释放。 > > 以下是一些原因: > > * 构造函数和析构函数: malloc 分配的内存是未初始化的,而 new 运算符分配的内存会调用对象的构造函数进行初始化。同样,free 不会调用对象的析构函数,而 delete 运算符会。 > > * 类型安全: new 和 delete 是类型安全的,它们在运行时处理类型信息,可以正确地匹配对象的大小并执行适当的构造和析构。malloc 和 free 不具备这种类型安全性。 > > * 异常处理: 如果 new 运算符分配失败,它会抛出 std::bad_alloc 异常,允许你在异常处理中采取适当的措施。malloc 返回 NULL,需要显式地检查返回值。 > > * 数组: new 和 delete 可以方便地用于动态数组的分配和释放,而 malloc 和 free 不支持数组的构造和析构。 > > * 更直观: 在 C++ 中,使用 new 和 delete 更符合 C++ 的设计理念,更直观地表达了动态分配和释放内存的目的。 > > 综上所述,尽管在 C++ 中可以使用 malloc 和 free,但通常更推荐使用 new 和 delete 运算符,因为它们提供了更好的类型安全性、更好的异常处理机制,同时更符合 C++ 的设计理念。 若要使用malloc 需要改寫為 例1 `int *x = (int*)malloc(sizeof(int))` 例2 `struct Node *node = (Node*)malloc(sizeof(struct Node));` ## ( ** ) 指標的指標/雙重指標 透過雙重指標改變指標變數的值 ```c= void foo(int** p) { *p = malloc(sizeof(int)); //改變*p的值 } ``` 如果指標要取得struct結構的成員要用->來存取成員 ```c++= typedef struct node{int data; struct node* next; }NODE; //NODE 變數 NODE* node; node->data = data; //存取成員data node->next = *top; //賦值 ``` ## const修飾符+指標 * `const int* p;` 表示p是一個指標,p本身平凡無比,但是p所指向的物件是一個特殊的物件-整型常量 (不能賦值) * `int* const p;` 這個p指標不是一個普通的指標,它是個常量指標,即只能對其初始化,而不能賦值,另外,這個指標所指向的物件是一平凡的int型變數 (只能初始化) * `const int* const p;` 結合前者兩種,指標和指向的物件都非同尋常,都是常量 ## 存取權限 ![](https://i.imgur.com/uLJenUv.png) ## operator overloading (運算子重載) 如果沒加&的話就會是複製的而不是原本的 通常運算子重載的參數要加const,避免被意外的更改 回傳值也通常是用*this,以便可以連續賦值多個對象 ```c++= #include <iostream> class A { public: int x; A(int x) : x(x) {} A& operator=(const A& other) { x = other.x; return *this; } }; int main() { A a(10); A b(20); b = a; std::cout << b.x << std::endl; // 输出10 return 0; } ``` ## override/overload/Polymorphism 覆寫/多載/多型 https://medium.com/@c824751/override-%E8%A6%86%E5%AF%AB-overload-%E5%A4%9A%E8%BC%89-polymorphism-%E5%A4%9A%E5%9E%8B-%E7%9A%84%E5%B7%AE%E7%95%B0-3de1a499de29 ### override 覆寫 Number *r = new Complex(8,9); ```c++= class Number{ private: int a; public: Number():a(7){} int get(){return a;} int setA(int v){a = v;} }; class Complex : public Number{ private: int *b; public: Complex(){ setA(4); b = new int(5); } Complex(int x,int y){ setA(x); b = new int(y); } int get(){return b[0];} }; int main(){ Number *r = new Complex(8,9); cout<<r->get()<<endl; } ``` 因為Number *r = new Complex(8,9); 當去掉用 r的物件變數時只能使用Number的成員函式 父類別的物件變數只能用父類別的成員變數及成員函式 子類別則可以使用父類別的 其他觀念:如果該function 在父類有加virtual 則會被覆寫,執行子類的function ### 多載 為何會有多載? 當今天一個功能需要因為不同需求帶入不同的參數列時,思考這樣需要多少個函式來做 多載可以根據帶進來的參數列決定要做哪個函式 ## Constructor/Destructor (建構子/解構子) - 順序 建構子 由上而下 解構子 由下而上 子類別到父類別 一般類別生命週期結束會自動呼叫解構子 delete 時會呼叫解構子 ## 淺層拷貝/深層拷貝 c++在傳入參數時需要加const ## 虛擬函式 * virtual 建構子 * virtual 解構子 * virtual 解構子 = 0 (純虛擬函式) * 補充:const = 0; (不能改變值) 像是110 6-6:const = 0,可以透過 double perimeter() const override 來判斷要加const,因為如果父類別加上const 子類別也要加上const修飾詞 還沒要在該類別中實作 純虛擬函式是為override ## 繼承建構子 看繼承 ```c++= Bird(double weight, string name, bool canFly):Pet(weight, name){ ... } ``` ## new ```c++= Circle *c1 = new Shape(); // 建立一個Circle類別型別的c1指標變數,並指向一個新創建的Shape類別 ``` 前提是 Shape必須是Circle的子類別,否則非法,因為兩者不同型別 例如: ```c++= Circle c1; Shape s1; s1 = c1; //不同型別,否則是繼承關係,或用重載運算子 ``` 例如: ```c++= #include <iostream> class A { public: int x; A(int x) : x(x) {} A& operator=(const A& other) { x = other.x; return *this; } }; int main() { A a(10); A b(20); b = a; std::cout << b.x << std::endl; // 输出10 return 0; } ``` 其他補充:如果一個類別作為父類別,且繼承後,該父類別不用建立沒關係 ## 建構子初始化方式 預設建構子 ```c++= class Box { public: Box() { /*perform any required default initialization steps*/} // All params have default values Box (int w = 1, int l = 1, int h = 1): m_width(w), m_height(h), m_length(l){} ... } ``` 多載範例 ```c++= class Box { public: // Default constructor Box() {} // Initialize a Box with equal dimensions (i.e. a cube) explicit Box(int i) : m_width(i), m_length(i), m_height(i) // member init list {} // Initialize a Box with custom dimensions Box(int width, int length, int height) : m_width(width), m_length(length), m_height(height) {} int Volume() { return m_width * m_length * m_height; } private: // Will have value of 0 when default constructor is called. // If we didn't zero-init here, default constructor would // leave them uninitialized with garbage values. int m_width{ 0 }; int m_length{ 0 }; int m_height{ 0 }; }; ``` ## vector 函式庫 ![](https://i.imgur.com/S5z0xgu.png) ```c++= vector<int> my_vec; my_vec.push_back(9); //由前面插入 9,4,5,... my_vec.pop_back(); //由後面end取出 my_vec.at(0); //at在編譯時會去check範圍,若超出範圍..較嚴謹 my_vec.front(); //第一個元素 my_vec.back(); //最後一個元素 my_vec.size(); //回傳size,{9,4,5,3}回傳4 vector<int> vec(5,6); //給予5個空間,初始值是6,vec={6,6,6,6,6} vec.insert(vec.begin()+1, 0); //在第一筆資料+1,插入0,{6,0,6,6,6,6} vec.erase(vec.begin()); //刪除第一筆資料(begin),vec={0,6,6,6,6} vec.erase(vec.begin(), vec.begin()+2); // 刪除begin到begin下兩個位置的資料,不包含begin下兩個位置的資料,所以6,6,6 sort(my_vec.begin(), my_vec.end()); //從begin到end排序 iterator有較高的通用性,有些容器不支援隨機存取像陣列 my_list_1.splice(my_list_1.end(), my_list_2); //splice,mylist2資料連接到mylist1的end,mylist1會變成mylist1+mylist2,mylist2會變空值 ``` 關於vector迭代器類型: ```c++ for(vector<int>::iterator it = number.begin(); it != number.end(); it++) { auto n = *it; 因為it迭代器是一種抽象概念不屬於任何型別,所以需要透過改變位置方式來賦予n cout << n << endl; } ``` ## 字串組合 注意會有overflow(溢位) strcat strcpy * 字串比較:strcmp(const char *str1, const char *str2): * 如果 str1 < str2 返回值<0 * 如果 str1 > str2 返回值>0 * 如果相等則 =0 ## template 模板 根據不一樣的參數列帶進去,產生不一樣的函數出來 ```c++= template<class T> T max(T a, T b){ //T型態 return (a>b)?a:b; } double a,b,c; int d,e,f; a = max<double>(b,c); //所有T被置換成double型態 d = max<int>(e,f); //被置換成int型態 ``` ## 其他 ### 輾轉相除法 ```pytoh= # 找質數 f(20) 輸出 [2,3,5,7,11,13,17,19] def g(N): for i in range(2,N): if N%i == 0: return False return True def f(N): data = [i for i in range(2,N) if g(i) == True] return data ``` ### 排列組合 ```python= # 排列組合 h('ABCD') is ["ABCD","BACD","CABD","DABC"] def h(data): result = [] length = len(data) for s in range(length): x = data[s] y = data[0:s] + data[s+1:length] print(data[5:length]) result = result + [x+y] return result ``` ### ASCII 0:48 A:65 a:97 ### c++陣列取長度 ```c++= // 111 problem 3 typedef struct student{ char id[10]; char name[30]; float grade; int quizs[NUM_OF_QUIZ]; }STUDENT; STUDENT students[] = {{"t011","John",0.0,{80,85,70}}, {"t020","Eric",0.0,{90,95,80}}, {"t022","Mary",0.0,{90,95,90}}, {"t003","Apple",0.0,{90,85,80}} }; numOfStudents = sizeof(students)/sizeof(students[0]); ``` ### 運算子 & 其他基本觀念(陷阱) * ~ 二進制倒數 * ! 十進制 非0則1 非1則0,例如:12->0 * << 二進制遞增,例 c<<2 c遞增兩位 * %3.1f 共3位保留小數點後一位 * 被除數、除數都是整數,結果也會是整數。 * double 型別如果運算值是整數則回傳結果也會是整數,除非是float才會變小數 ### enum 依照前數值遞增,若沒初始值就從0開始 ```c++= enum FOOD {fish=-1, eggs, meat=3, milk, bean}; // eggs為0 // milk為4 // bean為5 ``` ### 問函式型態 看return回傳值 ### 作用域 區域變數觀念,函式帶的參數是copy一份到函式內,return後由外面拿出來要在賦值才能改變全域變數的值 ### python陣列索引 [起始:結束:每往後跳幾個索引] 結果值不會包含結束,但會包含起始 ### lambda ```python= # Lambda function to check if a given vaue is from 10 to 20. test = lambda x : True if (x > 10 and x < 20) else False # Check if given numbers are in range using lambda function print(test(12)) print(test(3)) print(test(24)) ``` ``` True False False ```