# [C++] 結構與類別(struct & class) 在C++中,結構(struct)與類別(class)的設計,是為了讓開發者可以定義自己的資料型別(Data Type),如此一來,程式碼可以更容易被撰寫、除錯、維護。 ## 定義自己的資料型別:使用struct ### 宣告struct `struct`可以讓使用者創造自己定義的資料型別,但無法定義函式 例如: ``` C++= struct Sales_data { string book_no; unsigned unit_sold = 0; double revenue = 0.0; }; ``` 其中`book_no`, `unit_sold `, `revenue`是這個struct的data members,簡稱members ### 呼叫struct `Sales_data.h` ``` c++= #ifndef SALES_DATA_H #define SALES_DATA_H #include <string> struct Sales_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; #endif ``` `main.cpp` ``` c++= #include <iostream> #include "Sales_data.h" using namespace std; int main() { Sales_data data1; double price = 0; cout << "Please enter Book No., Unit of sold, and price> "; cin >> data1.bookNo >> data1.units_sold >> price; data1.revenue = data1.units_sold * price; cout << "Revenue: " <<data1.revenue <<endl; } ``` ## 定義自己的資料型別:使用class ### struct跟class差異之處 | 不同之處 | struct | class | | -------- | -------- | -------- | | 型態 | 值類型 | 參考類型 | | 預設成員 | public | private | | 記憶體位置 | Stack上 | Heap上 | | 繼承 | 只能實現Interface | 可繼承也可實現Interface | | NULL | 不能NULL | 可為NULL | ### 宣告class 透過`class`,使用者可以定義自己的變數與函式成員 宣告形式如下: ``` c++= class class_name { int x, y; #預設為private public: int member1; private: int member2; protected: int member2; }; ``` - 其中成員的權限範圍說明如下,如果沒有宣告範圍則預設為private - public: 任何一個class都可以使用 - private: 只有同一個class的其他成員或該class的friend class可以使用 - protected: 只有同一個class的其他成員、該class的friend class或derived class可以使用 ### 使用class定義物件 - 使用constructor初始化物件 在class內部,只定義prototype,在class外才定義內容。 其中::是範圍運算子,賦予成員適當的範圍屬性,也就是所屬的class ``` c++= #include <iostream> using namespace std; class CRectangle{ int width, height; public: CRectangle (int,int); //constructor prototype int area (void){return (width*height);} }; //constructor CRectangle::CRectangle (int a, int b){ //constructor 內容 width = a; height = b; } int main() { CRectangle rectA(3,4); cout << "area: " << rectA.area() << endl; } ``` - Overloading constructor初始化物件 ``` c++= #include <iostream> using namespace std; class CRectangle{ int width, height; public: CRectangle (); CRectangle (int,int); int area (void){return (width*height);} }; //overloading constructor CRectangle::CRectangle (){ width = 5; height = 5; } CRectangle::CRectangle (int a, int b){ width = a; height = b; } int main() { CRectangle rectA(3,4); CRectangle rectB; cout << "area of rect A: " << rectA.area() << endl; cout << "area of rect B: " << rectB.area() << endl; } ``` 也可以這樣寫 ``` c++= #include <iostream> using namespace std; class CRectangle{ int width, height; public: CRectangle (): width(5), height(5){}; //contructor without parameter CRectangle (int,int); //contructor with parameter int area (void){return (width*height);} }; //constructor CRectangle::CRectangle (int a, int b){ //constructor 內容 width = a; height = b; } int main() { CRectangle rectA; CRectangle rectB(4,6); cout << "area: " << rectA.area() << endl; cout << "area: " << rectB.area() << endl; } ``` ### 類別中的指標 (Pointer to class) ``` c++= #include <iostream> using namespace std; class CRectangle{ int width, height; public: void set_values(int, int); int area (void){return (width*height);} }; void CRectangle::set_values(int a, int b){ width = a; height = b; } int main() { CRectangle a, *b, *c; CRectangle *d = new CRectangle[2]; b = new CRectangle; a.set_values(1, 2); b->set_values(3, 4); c = &a; d->set_values(5, 6); d[1].set_values(7, 8); cout << "area of a: " << a.area() << endl; cout << "area of *b: " << b->area() << endl; cout << "area of *c: " << c->area() << endl; cout << "area of d[0]: " << d[0].area() << endl; cout << "area of d[1]: " << d[1].area() << endl; return 0; } ``` - 類別中指標的解讀方法 - *x: pointed by x - &x: address of x - x.y: member y of object x - (*x).y: member y of object pointed by x - x->y: 跟(*x).y一樣 member y of object pointed by x - x[n]: (n+1)th object pointed by x ### 關鍵字 this 關鍵字this是一個指標,被用於一個class內部,指正在被執行的物件的記憶體位址。 主要用途有兩個: - 用來檢查傳入的物件程員函數的參數,是否是該物件本身 ```c++= #include <iostream> using namespace std; class CDummy{ public: int isitme(CDummy& param); }; int CDummy::isitme(CDummy& param){ if (&param == this) //this就是&param return 1; else return 0; } int main() { CDummy a; CDummy *b = &a; if(b->isitme(a)) cout << "yes, &a is b"<<endl; return 0; } ``` - 用來return物件的指標 ```c++= CVector& Cvector::operator= (const CVector& param){ x=param.x; y=param.y; return *this; } ``` ### Static Members(靜態成員) 靜態成員可以是資料變數,也可以是函式,對同一個class產生的所有物件,都可以共用這個成員,所以又稱class variable。 常見用法是計算一個class產生的物件個數,如下,這個n就是class內全部成員共用。 ```c++= #include <iostream> using namespace std; class CDummy{ public: static int n; //用static方式宣告 CDummy (){ n++;}; ~CDummy (){ n--;}; }; int CDummy::n=0; int main() { CDummy a; CDummy b[5]; CDummy *c = new CDummy; cout << a.n << endl; delete c; cout << CDummy::n << endl; return 0; } ``` 執行結果如下: ![](https://i.imgur.com/u5UFyc8.png) ### 運算子多載 (Overloading Operators) 標準運算子,例如+ - * /可以用於基礎型別,但如果要讓類別/結構物件也可以運用這些運算子,可以使用運算子多載的語法 #### 宣告方式 `type operator sign (parameters);` - `type`是class名稱 - `operator`是保留字 - `sign`可以是 + - * / = < > Ex. 將+運算子進行多載 ```C++= #include <iostream> using namespace std; class CVector{ public: int x,y; CVector (); CVector (int, int); CVector operator + (CVector); }; CVector::CVector(){ x = 0; y = 0; } CVector::CVector(int a, int b){ x = a; y = b; } CVector CVector::operator+ (CVector param){ CVector temp; temp.x = x + param.x; temp.y = y + param.y; return (temp); } int main() { CVector a(3,1); CVector b(1,2); CVector c; c = a + b; cout << c.x << ", "<< c.y<<endl; return 0; } ``` ### 授權:Friend #### Friend Function 為了實現允許外部函數可以訪問class的private和protected成員,我們必須在class內部宣告該外部函數的prototype。 ```C++= #include <iostream> using namespace std; class CRectangle{ int width, height; public: void set_values (int, int); int area (void) {return (width * height);} friend CRectangle duplicate (CRectangle); //宣告friend function }; void CRectangle::set_values(int a, int b){ width = a; height = b; } //friend function可以存取private member: width, height CRectangle duplicate(CRectangle rectparam){ CRectangle rectres; rectres.width = rectparam.width*2; rectres.height = rectparam.height*2; return (rectres); } int main() { CRectangle rect_a, rect_b; rect_a.set_values(2,3); rect_b = duplicate(rect_a); cout << rect_b.area()<<endl; return 0; } ``` #### Friend class 我們也可以定義一個class是另一個class的friend,如此一來,friend class可以訪問第一個class protected和private成員。 Ex. 將`CSquare`設為`CRectangle`的friend class ```C++= #include <iostream> using namespace std; class CSquare; //必須加入,因為class CRectangle宣告有包括CSquare class CRectangle{ int width, height; public: int area (void) {return (width * height);} void convert (CSquare a); }; class CSquare { private: int side; public: void set_side (int a){side=a;} friend class CRectangle; }; void CRectangle::convert (CSquare a){ width = a.side; height = a.side; } int main() { CSquare sqr; CRectangle rect; sqr.set_side(4); rect.convert(sqr); cout << rect.area(); return 0; } ``` ### Inheritance (繼承) #### class之間的繼承 基於某個class生成新的class,新的class可以擁有前者的成員,又可以加上自己的成員。 被繼承的class,稱為base class,新生成的class,稱為derived class。 宣告語法如下: `class derived_class_name: 權限 base_class_name` 其中權限可以為public, private, protected,定義存取base class的權限 也就是說,從base class繼承過來的成員,會以指定權限存在著。 繼承範例如下: ``` c++= #include <iostream> using namespace std; //base class class CPolygon{ protected: int width, height; public: void set_values(int a, int b) { width = a; height = b;} }; //derived class class CRectangle:public CPolygon{ public: int area(void); }; int CRectangle::area(){ return (width*height); } //derived class class CTriangle:public CPolygon{ public: int area(void); }; int CTriangle::area(){ return (width*height/2); } int main() { CRectangle rect; CTriangle tri; rect.set_values(3, 4); tri.set_values(3, 4); cout << "Area of rect: " << rect.area()<<endl; cout << "Area of tri: " << tri.area()<<endl; return 0; } ``` #### 重要觀念1:總結成員權限 | 權限 | public | protected | private | | -------- | -------- | -------- | -------- | | class本身 | yes | yes | yes | | derived class | yes | yes | no | | class以外 | yes | no | no | #### 重要觀念2:derived class會從base class繼承的東西 預設來說 1. 會從base class繼承public、protected成員 2. 不會從base class繼承以下成員 - 有參數的constructor/deconstructor - overloading operator - friends #### 重要觀念3:當derived class生成物件時,base class也會同時調用建構子 ``` c++= #include <iostream> using namespace std; //base class class mother{ public: mother () { cout << "mother: no parameters\n";} mother (int a) { cout << "mother: int parameters\n";} }; //derived class class daughter:public mother{ public: daughter (int a){ cout<<"daughter: int parameter\n";} //預設mother無參數的建構子 }; //derived class class son:public mother{ public: son (int a):mother(a) { cout<<"son: int parameter\n";} //指定mother帶有參數的建構子 }; int main() { daughter mary (1); son tom(1); return 0; } ``` ![](https://i.imgur.com/NwBxRrf.png) #### 重要觀念3:C++ class支援多重繼承 多重繼承就是一個class,可以同時從多個class中繼承成員 範例如下: - class CRectangle同時繼承了CPolygon, COutput - class CTriangle同時繼承了CPolygon, COutput ``` c++= #include <iostream> using namespace std; //base class 1 class CPolygon{ protected: int width, height; public: void set_values(int a, int b) { width = a; height = b;} }; //base class 2 class COutput{ public: void output(int i) { cout << i << endl;} }; //derived class class CRectangle:public CPolygon, public COutput{ public: int area(void) {return (width*height);} }; //derived class class CTriangle:public CPolygon, public COutput{ public: int area(void) {return (width*height/2);} }; int main() { CRectangle rect; CTriangle tri; rect.set_values(3, 4); tri.set_values(3, 4); rect.output (rect.area()); tri.output (tri.area()); return 0; } ``` ### Pointers to base class (父類別指標) ``` c++= #include <iostream> using namespace std; //base class class CPolygon{ protected: int width, height; public: void set_values(int a, int b) { width = a; height = b;} }; //derived class class CRectangle:public CPolygon{ public: int area(void) {return (width*height);} }; //derived class class CTriangle:public CPolygon{ public: int area(void) {return (width*height/2);} }; int main() { CRectangle rect; CTriangle tri; CPolygon * ppoly1 = &rect; //rect is pointed by ppoly1 CPolygon * ppoly2 = &tri; //tri is pointed by ppoly2 ppoly1->set_values(3, 4); ppoly2->set_values(3, 4); cout << rect.area() << endl; cout << tri.area() << endl; return 0; } ``` ### Polymorphism(多形) #### Virtual Members: 先定義函數成員的prototype ``` c++= #include <iostream> using namespace std; //base class class CPolygon{ protected: int width, height; public: void set_values(int a, int b) { width = a; height = b;} virtual int area(void) {return 0;} //先定義prototype }; //derived class class CRectangle:public CPolygon{ public: int area(void) {return (width*height);} }; //derived class class CTriangle:public CPolygon{ public: int area(void) {return (width*height/2);} }; int main() { CRectangle rect; CTriangle tri; CPolygon poly; CPolygon *ppoly1 = &rect; CPolygon *ppoly2 = &tri; CPolygon *ppoly3 = &poly; ppoly1->set_values(3, 4); ppoly2->set_values(3, 4); ppoly3->set_values(3, 4); cout << ppoly1->area() << endl; cout << ppoly2->area() << endl; cout << ppoly3->area() << endl; return 0; } ``` #### Abstract base class ``` c++= #include <iostream> using namespace std; //base class class CPolygon{ protected: int width, height; public: void set_values(int a, int b) { width = a; height = b;} virtual int area(void) = 0; //先定義prototype void printarea (void){ cout << this->area() << endl; } }; //derived class class CRectangle:public CPolygon{ public: int area(void) {return (width*height);} //derived class的實作 }; //derived class class CTriangle:public CPolygon{ public: int area(void) {return (width*height/2);} //derived class的實作 }; int main() { CRectangle rect; CTriangle tri; CPolygon *ppoly1 = &rect; CPolygon *ppoly2 = &tri; ppoly1->set_values(3, 4); ppoly2->set_values(3, 4); // 不用考慮具體是哪一個derived class的area()實作,會自動對應 ppoly1->printarea(); //print 12 ppoly2->printarea(); //print 6 return 0; } ``` ## 實務:使用C++實作資料結構Linked List 參考資料:http://alrightchiu.github.io/SecondRound/linked-list-xin-zeng-zi-liao-shan-chu-zi-liao-fan-zhuan.html ## 參考資料 C++ Primer 5th version - CH2.6 定義我們自己的資料結構 - CH7 類別 ###### tags: ` C/C++程式語言觀念`