# [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 (¶m == this) //this就是¶m
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 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 = ▭
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 = ▭
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++程式語言觀念`