上一講雞啄米給大家講了構造函數和析構函數,讓大家了解了類的創建和釋放過程。這一節講講類的組合。

       在我們對現實中的某些事物抽象成類時,可能會形成很復雜的類,為了更簡潔的進行軟件開發,我們經常把其中相對比較獨立的部分拿出來定義成一個個簡單的類,這些比較簡單的類又可以分出更簡單的類,最后由這些簡單的類再組成我們想要的類。比如,我們想要創建一個計算機系統的類,首先計算機由硬件和軟件組成,硬件又分為CPU、存儲器等,軟件分為系統軟件和應用軟件,如果我們直接創建這個類是不是很復雜?這時候我們就可以將CPU寫一個類,存儲器寫一個類,其他硬件每個都寫一個類,硬件類就是所有這些類的組合,軟件也是一樣,也能做成一個類的組合。計算機類又是硬件類和軟件類的組合。

       類的組合其實描述的就是在一個類里內嵌了其他類的對象作為成員的情況,它們之間的關系是一種包含與被包含的關系。簡單說,一個類中有若干數據成員是其他類的對象。以前的教程中我們看到的類的數據成員都是基本數據類型的或自定義數據類型的,比如int、float類型的或結構體類型的,現在我們知道了,數據成員也可以是類類型的。

       如果在一個類中內嵌了其他類的對象,那么創建這個類的對象時,其中的內嵌對象也會被自動創建。因為內嵌對象是組合類的對象的一部分,所以在構造組合類的對象時不但要對基本數據類型的成員進行初始化,還要對內嵌對象成員進行初始化。

       組合類構造函數定義(注意不是聲明)的一般形式為:

       類名::類名(形參表):內嵌對象1(形參表),內嵌對象2(形參表),...
       {
                 類的初始化
       }

       其中,“內嵌對象1(形參表),內嵌對象2(形參表),...”成為初始化列表,可以用于完成對內嵌對象的初始化。其實,一般的數據成員也可以這樣初始化,就是把這里的內嵌對象都換成一般的數據成員,后面的形參表換成用來的初始化一般數據成員的變量形參,比如,Point::Point(int xx, int yy):X(xx),Y(yy) { },這個定義應該怎么理解呢?就是我們在構造Point類的對象時傳入實參初始化xx和yy,然后用xx的值初始化Point類的數據成員X,用yy的值初始化數據成員Y。

       聲明一個組合類的對象時,不僅它自身的構造函數會被調用,還會調用其內嵌對象的構造函數。那么,這些構造函數的調用是什么順序呢?首先,根據前面說的初始化列表,按照內嵌對象在組合類的聲明中出現的次序,依次調用內嵌對象的構造函數,然后再執行本類的構造函數的函數體。比如下面例子中對于Distance類中的p1和p2就是先調用p1的構造函數,再調用p2的構造函數。因為Point p1,p2;是先聲明的p1后聲明的p2。最后才是執行Distance構造函數的函數體。

       如果聲明組合類的對象時沒有指定對象的初始值的話,就會自動調用無形參的構造函數,構造內嵌對象時也會對應的調用內嵌對象的無形參的構造函數。析構函數的執行順序與構造函數正好相反。

雞啄米:C++編程入門系列之十五(類與對象:類的組合)

       這里雞啄米給大家一個類的組合的例子,其中,Distance類就是組合類,可以計算兩個點的距離,它包含了Point類的兩個對象p1和p2。

       #include <iostream>
       using namespace std;

       class Point
       {
       public:
                 Point(int xx,int yy)   { X=xx; Y=yy; } //構造函數
                 Point(Point &p);
                 int GetX(void)     { return X; }        //取X坐標
                 int GetY(void)     { return Y; } //取Y坐標
       private:
                 int X,Y; //點的坐標
       };

       Point::Point(Point &p)
       {
                 X = p.X;
                 Y = p.Y;
                 cout << "Point拷貝構造函數被調用" << endl;
       }

       class Distance
       {
       public:
                Distance(Point a,Point b); //構造函數
                double GetDis()   { return dist; }
       private:
                Point  p1,p2;
                double dist;               // 距離
        };
        // 組合類的構造函數
        Distance::Distance(Point a, Point b):p1(a),p2(b)
        {
                cout << "Distance構造函數被調用" << endl;
                double x = double(p1.GetX() - p2.GetX());
                double y = double(p1.GetY() - p2.GetY());
                dist = sqrt(x*x + y*y);
                return;
        }

        int _tmain(int argc, _TCHAR* argv[])
        {
               Point myp1(1,1), myp2(4,5);
               Distance myd(myp1, myp2);
               cout << "The distance is:";
               cout << myd.GetDis() << endl;
               return 0;
        }

       這段程序的運行結果是:
       Point拷貝構造函數被調用
       Point拷貝構造函數被調用
       Point拷貝構造函數被調用
       Point拷貝構造函數被調用
       Distance構造函數被調用
       The distance is:5

       Point類的構造函數是內聯成員函數,內聯成員函數在雞啄米:C++編程入門系列之十三(類與對象:類的聲明、成員的訪問控制和對象)中已經講過。
       雞啄米給大家分析下這個程序,首先生成兩個Point類的對象,然后構造Distance類的對象myd,最后輸出兩點的距離。Point類的拷貝構造函數被調用了4次,而且都是在Distance類構造函數執行之前進行的,在Distance構造函數進行實參和形參的結合時,也就是傳入myp1和myp2的值時調用了兩次,在用傳入的值初始化內嵌對象p1和p2時又調用了兩次。兩點的距離在Distance的構造函數中計算出來,存放在其私有數據成員dist中,只能通過公有成員函數GetDis()來訪問。

       雞啄米再跟大家說下類組合時的一種特殊情況,就是兩個類可能相互包含,即類A中有類B類型的內嵌對象,類B中也有A類型的內嵌對象。我們知道,C++中,要使用一個類必須在使用前已經聲明了該類,但是兩個類互相包含時就肯定有一個類在定義之前就被引用了,這時候怎么辦呢?就要用到前向引用聲明了。前向引用聲明是在引用沒有定義的類之前對該類進行聲明,這只是為程序聲明一個代表該類的標識符,類的具體定義可以在程序的其他地方,簡單說,就是聲明下這個標識符是個類,它的定義你可以在別的地方找到。

       比如,類A的公有成員函數f的形參是類B的對象,同時類B的公有成員函數g的形參是類A的對象,這時就必須使用前向引用聲明:

       class B;  //前向引用聲明
       class A
       { 
       public:
                   void f(B b);
       };
       class B
       { 
       public:
                  void g(A a);
       };

       這段程序的第一行給出了類B的前向引用聲明,說明B是一個類,它具有類的所有屬性,具體的定義在其他地方。

       今天雞啄米講了類的組合,這個知識還是很實用的,可以讓我們的程序更靈活,不至于太龐雜讓人摸不著頭腦。面向對象是不是很強大啊?希望大家從思想上領會它。
 

除非特別注明,雞啄米文章均為原創
轉載請標明本文地址:http://www.vkzldl.live/software/53.html
2011年9月24日
作者:雞啄米 分類:軟件開發 瀏覽: 評論:19