C++類和對象的基本簡介,包括構造函數、析構函數、拷貝構造函數、友元函數、內聯函數、類的this指針、靜態成員等內容概念介紹。 ...
說明:本文章內容大多來源於網路(http://www.runoob.com/cplusplus/cpp-classes-objects.html),在此整理記錄自己的學習過程。
一、 簡介
1.類用於指定對象的形式,它包含了數據表示法和用於處理數據的方法。類中的數據和方法稱為類的成員。函數在一個類中被稱為類的成員。
2.類提供了對象的藍圖,所以基本上,對象是根據類來創建的。聲明類的對象,就像聲明基本類型的變數一樣。
二、類成員函數
1.類的成員函數是指那些把定義和原型寫在類定義內部的函數,就像類定義中的其他變數一樣。類成員函數是類的一個成員,它可以操作類的任意對象,可以訪問對象中的所有成員。
2.成員函數可以定義在類定義內部,或者單獨使用範圍解析運算符 :: 來定義。在類定義中定義的成員函數把函數聲明為內聯的,即便沒有使用 inline 標識符。
1 class Box 2 { 3 public: 4 double length; // 長度 5 double breadth; // 寬度 6 double height; // 高度 7 8 double getVolume(void) 9 { 10 return length * breadth * height; 11 } 12 };
或者
1 double Box::getVolume(void) 2 { 3 return length * breadth * height; 4 }
三、構造函數
1.類的構造函數是類的一種特殊的成員函數,它會在每次創建類的新對象時執行。
2.構造函數的名稱與類的名稱是完全相同的,並且不會返回任何類型,也不會返回 void。構造函數可用於為某些成員變數設置初始值。
四、析構函數
1.類的析構函數是類的一種特殊的成員函數,它會在每次刪除所創建的對象時執行。
2.析構函數的名稱與類的名稱是完全相同的,只是在前面加了個波浪號(~)作為首碼,它不會返回任何值,也不能帶有任何參數。析構函數有助於在跳出程式(比如關閉文件、釋放記憶體等)前釋放資源。
註:刪除類對象時會調用析構函數;程式跳出對象作用域時會調用析構函數。
五、拷貝構造函數
拷貝構造函數是一種特殊的構造函數,它在創建對象時,是使用同一類中之前創建的對象來初始化新創建的對象。拷貝構造函數通常用於:
-
通過使用另一個同類型的對象來初始化新創建的對象。
1 Box box2=box1;
-
複製對象把它作為參數傳遞給函數。
1 printwidth(box1);
-
複製對象,並從函數返回這個對象。
如果在類中沒有定義拷貝構造函數,編譯器會自行定義一個。如果類帶有指針變數,並有動態記憶體分配,則它必須有一個拷貝構造函數。拷貝構造函數的最常見形式如下:
1 classname (const classname &obj) {//obj 是一個對象引用,該對象是用於初始化另一個對象的。 2 // 構造函數的主體 3 }
六、友元函數
類的友元函數是定義在類外部,但有權訪問類的所有私有(private)成員和保護(protected)成員。儘管友元函數的原型有在類的定義中出現過,但是友元函數並不是成員函數。
友元可以是一個函數,該函數被稱為友元函數;友元也可以是一個類,該類被稱為友元類,在這種情況下,整個類及其所有成員都是友元。
如果要聲明函數為一個類的友元,需要在類定義中該函數原型前使用關鍵字 friend,如下所示:
1 class Box 2 { 3 double width; 4 public: 5 double length; 6 friend void printWidth( Box box ); 7 void setWidth( double wid ); 8 };
聲明類 ClassTwo 的所有成員函數作為類 ClassOne 的友元,需要在類 ClassOne 的定義中放置如下聲明:
1 friend class ClassTwo;
七、內聯函數
1.C++ 內聯函數是通常與類一起使用。如果一個函數是內聯的,那麼在編譯時,編譯器會把該函數的代碼副本放置在每個調用該函數的地方。
2.對內聯函數進行任何修改,都需要重新編譯函數的所有客戶端,因為編譯器需要重新更換一次所有的代碼,否則將會繼續使用舊的函數。
3.如果想把一個函數定義為內聯函數,則需要在函數名前面放置關鍵字 inline,在調用函數之前需要對函數進行定義。如果已定義的函數多於一行,編譯器會忽略 inline 限定符。
4.在類定義中的定義的函數都是內聯函數,即使沒有使用 inline 說明符。
Tip:
只有當函數只有 10 行甚至更少時才將其定義為內聯函數.
定義: 當函數被聲明為內聯函數之後, 編譯器會將其內聯展開, 而不是按通常的函數調用機制進行調用.
優點: 當函數體比較小的時候, 內聯該函數可以令目標代碼更加高效. 對於存取函數以及其它函數體比較短, 性能關鍵的函數, 鼓勵使用內聯.
缺點: 濫用內聯將導致程式變慢. 內聯可能使目標代碼量或增或減, 這取決於內聯函數的大小. 內聯非常短小的存取函數通常會減少代碼大小, 但內聯一個相當大的函數將戲劇性的增加代碼大小. 現代處理器由於更好的利用了指令緩存, 小巧的代碼往往執行更快。
結論: 一個較為合理的經驗準則是, 不要內聯超過 10 行的函數. 謹慎對待析構函數, 析構函數往往比其錶面看起來要更長, 因為有隱含的成員和基類析構函數被調用!
另一個實用的經驗準則: 內聯那些包含迴圈或 switch 語句的函數常常是得不償失 (除非在大多數情況下, 這些迴圈或 switch 語句從不被執行).
有些函數即使聲明為內聯的也不一定會被編譯器內聯, 這點很重要; 比如虛函數和遞歸函數就不會被正常內聯. 通常, 遞歸函數不應該聲明成內聯函數.(遞歸調用堆棧的展開並不像迴圈那麼簡單, 比如遞歸層數在編譯時可能是未知的, 大多數編譯器都不支持內聯遞歸函數). 虛函數內聯的主要原因則是想把它的函數體放在類定義內, 為了圖個方便, 抑或是當作文檔描述其行為, 比如精短的存取函數.
八、this指針
在 C++ 中,每一個對象都能通過 this 指針來訪問自己的地址。this 指針是所有成員函數的隱含參數。因此,在成員函數內部,它可以用來指向調用對象。
友元函數沒有 this 指針,因為友元不是類的成員。只有成員函數才有 this 指針。
下麵的實例有助於更好地理解 this 指針的概念:
1 #include <iostream> 2 3 using namespace std; 4 5 class Box 6 { 7 public: 8 // 構造函數定義 9 Box(double l=2.0, double b=2.0, double h=2.0) 10 { 11 cout <<"Constructor called." << endl; 12 length = l; 13 breadth = b; 14 height = h; 15 } 16 double Volume() 17 { 18 return length * breadth * height; 19 } 20 int compare(Box box) 21 { 22 return this->Volume() > box.Volume(); 23 } 24 private: 25 double length; // Length of a box 26 double breadth; // Breadth of a box 27 double height; // Height of a box 28 }; 29 30 int main(void) 31 { 32 Box Box1(3.3, 1.2, 1.5); // Declare box1 33 Box Box2(8.5, 6.0, 2.0); // Declare box2 34 35 if(Box1.compare(Box2)) 36 { 37 cout << "Box2 is smaller than Box1" <<endl; 38 } 39 else 40 { 41 cout << "Box2 is equal to or larger than Box1" <<endl; 42 } 43 return 0; 44 }
編譯執行結果
1 Constructor called. 2 Constructor called. 3 Box2 is equal to or larger than Box1
九、指向類的指針
一個指向 C++ 類的指針與指向結構的指針類似,訪問指向類的指針的成員,需要使用成員訪問運算符 ->,就像訪問指向結構的指針一樣。與所有的指針一樣,您必須在使用指針之前,對指針進行初始化。
十、類的靜態成員
1.使用 static 關鍵字可以把類成員定義為靜態的。當我們聲明類的成員為靜態時,這意味著無論創建多少個類的對象,靜態成員都只有一個副本。
2.靜態成員在類的所有對象中是共用的。如果不存在其他的初始化語句,在創建第一個對象時,所有的靜態數據都會被初始化為零。
3.不能把靜態成員的初始化放置在類的定義中,但是可以在類的外部通過使用範圍解析運算符 :: 來重新聲明靜態變數從而對它進行初始化
如果把函數成員聲明為靜態的,就可以把函數與類的任何特定對象獨立開來。靜態成員函數即使在類對象不存在的情況下也能被調用,靜態函數只要使用類名加範圍解析運算符 :: 就可以訪問。
靜態成員函數只能訪問靜態成員數據、其他靜態成員函數和類外部的其他函數。
靜態成員函數有一個類範圍,他們不能訪問類的 this 指針。您可以使用靜態成員函數來判斷類的某些對象是否已被創建。
靜態成員函數與普通成員函數的區別:
- 靜態成員函數沒有 this 指針,只能訪問靜態成員(包括靜態成員變數和靜態成員函數)。
- 普通成員函數有 this 指針,可以訪問類中的任意成員;而靜態成員函數沒有 this 指針。
1 #include <iostream> 2 3 using namespace std; 4 5 class Box 6 { 7 public: 8 static int objectCount; 9 // 構造函數定義 10 Box(double l=2.0, double b=2.0, double h=2.0) 11 { 12 cout <<"Constructor called." << endl; 13 length = l; 14 breadth = b; 15 height = h; 16 // 每次創建對象時增加 1 17 objectCount++; 18 } 19 double Volume() 20 { 21 return length * breadth * height; 22 } 23 static int getCount() 24 { 25 return objectCount; 26 } 27 private: 28 double length; // 長度 29 double breadth; // 寬度 30 double height; // 高度 31 }; 32 33 // 初始化類 Box 的靜態成員 34 int Box::objectCount = 0; 35 36 int main(void) 37 { 38 39 // 在創建對象之前輸出對象的總數 40 cout << "Inital Stage Count: " << Box::getCount() << endl; 41 42 Box Box1(3.3, 1.2, 1.5); // 聲明 box1 43 Box Box2(8.5, 6.0, 2.0); // 聲明 box2 44 45 // 在創建對象之後輸出對象的總數 46 cout << "Final Stage Count: " << Box::getCount() << endl; 47 48 return 0; 49 }
編譯執行結果
1 Inital Stage Count: 0 2 Constructor called. 3 Constructor called. 4 Final Stage Count: 2
十一、綜合測試實驗
執行測試代碼,及其輸出結果如下所示:
1 #include <iostream> 2 3 using namespace std; 4 5 class Box{ 6 double width;//預設訪問修飾符為private 7 double *high;//【當類成員變數是指針時,需在構造函數和拷貝構造函數中為其申請記憶體,併在析構函數中刪除記憶體】 8 public: 9 void setWidth(double wid); 10 void setHigh(double hig); 11 double getHigh( void ); 12 Box(double wid, double hig);//構造函數 13 ~Box(void);//析構函數 14 Box(const Box &obj);//拷貝構造函數 15 16 friend void printwidth(Box box);//友元函數 17 }; 18 void Box::setWidth(double wid) 19 { 20 width = wid; 21 } 22 void Box::setHigh(double hig) 23 { 24 *high = hig; 25 } 26 double Box::getHigh( void ) 27 { 28 return *high; 29 } 30 Box::Box(double wid, double hig) 31 { 32 cout << "調用構造函數" << endl; 33 width = wid; 34 high = new double;//申請記憶體 35 *high = hig; 36 } 37 Box::~Box(void) 38 { 39 cout << "調用析構函數並釋放記憶體" << endl; 40 delete high;//釋放記憶體 41 } 42 Box::Box(const Box &obj) 43 { 44 cout << "調用拷貝構造函數" << endl; 45 width = obj.width; 46 high = new double;//申請記憶體 47 *high = *obj.high; 48 } 49 //友元函數不是類成員變數,但需在類中聲明 50 void printwidth(Box box) 51 { 52 cout << "調用友元函數" << endl; 53 cout << "width of box :" << box.width << endl; 54 } 55 void printhigh(Box obj) 56 { 57 cout << "high of box :" << obj.getHigh() << endl; 58 } 59 //程式主函數 60 int main( ) 61 { 62 Box box1(12,15); 63 Box box2=box1; 64 65 //輸出width、high 66 printwidth(box1); 67 printhigh(box1); 68 printwidth(box2); 69 printhigh(box2); 70 71 //設置width、high 72 box2.setWidth(13); 73 box2.setHigh(16); 74 75 //輸出width、high 76 printwidth(box1); 77 printhigh(box1); 78 printwidth(box2); 79 printhigh(box2); 80 81 return 0;//調用析構函數(釋放box1、box2) 82 }