C++ 類 & 對象


說明:本文章內容大多來源於網絡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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM