本人在學習Qt的時候發現了一個非常有趣的現象。有很多函數的調用方法都寫成了如下的形式:
object.func().func2();
這令小弟着實不懂。在上面這段代碼中,第一個對象調用它的成員函數func()是完全沒有問題的,但是后面那個func2()就奇怪了。我們只知道,點運算符(.)的作用就是調用對象的成員,但是如果按照上面這個程序的字面意思來理解,就是對象object調用它的成員函數func(),然后函數func()再調用它的成員函數func2()。這怎么能解釋得通哩??我們只知道對象有成員函數,但是從來沒有聽說過函數也可以有成員函數的啊。沒有辦法,只有翻C++的工具書,最后,居然發現了這個原來就是C++中的“包含”思想。那么究竟何為包含呢,且聽小弟慢慢敘來......^_^
何為“包含”,其實說白了就是一個類可以包含另一個類的對象。即如下程序所示:
class A { //... }; class B { //... A a; A b; };
在上面這個程序中,我們定義了類A和類B。其中類B里面我們定義了類A的兩個對象a和b。這樣的情況就叫類B包含了類A。
下面,我們用一個程序來看一下“包含”:
1 //#include <stdio.h> 2 //#include <stdlib.h> 3 #include <iostream> 4 using namespace std; 5 6 class A 7 { 8 public: 9 A(int i) :x(i) 10 { 11 cout << "A's constructor" << endl; 12 } 13 14 void getx() 15 { 16 cout << "A's a is" << x << endl; 17 } 18 ~A() 19 { 20 cout << "A's destructor" << endl; 21 } 22 23 private: 24 int x; 25 26 }; 27 28 class B 29 { 30 public: 31 B(int x,int z) :a(x), z(z) 32 { 33 cout << "B' constructor" << endl; 34 } 35 36 A& getA() 37 { 38 return a; 39 } 40 41 ~B() 42 { 43 cout << "B's destructor" << endl; 44 } 45 46 private: 47 A a; 48 int z; 49 50 51 }; 52 53 void playstage() 54 { 55 B b1(1,2); 56 b1.getA().getx(); 57 } 58 59 void main() 60 { 61 playstage(); 62 system("pause"); 63 64 65 }
- 首先對兩個類進行分析:在上面這個程序中,我們定義了兩個類A和B。其中可以看到,在類B的私有成員變量里面,我們定義了一個類B自己的成員變量,另外還定義了類A的對象a(47行);另外在類B的公有函數中,我們定義了返回值為類A的函數:getA(),它的作用就是返回在類B中定義的類A的對象a。在這里我們特別應該注意的是類B的構造函數:
B(int x,int z) :a(x), z(z)
這個構造函數很有意思。我們可以看到它不僅初始化了自己的私有成員變量z,而且也順帶初始化了類A的對象a。那么它肯定會調用類A的構造函數。然后再調用B類自己的構造函數。那么析構的時候順序應該就是相反的,首先調用B類的析構函數,然后再調用A類的析構函數。我們可以看到后面的程序輸出圖這樣說滴,^_^。(見輸出的紅色框)
此處怎么比我們預計的多了一項輸出呢?(箭頭所指處)。。
因為在調用b1.getA().getX()時,getA()返回了我們所創建的A類的對象a,此處相當於復制了一份(其實是調用了默認的copy構造函數)。所以我們最后需要多析構一次對象a.(作為返回值調用copy構造函數)
下面來印證我的猜想:
我們在類A中定義copy構造函數,其他代碼不變。
1 class A 2 { 3 public: 4 A(int i) :x(i) 5 { 6 cout << "A's constructor" << endl; 7 } 8 A(A &a) 9 { 10 x = a.x; 11 cout << "A's copy constructor" << endl; 12 } 13 void getx() 14 { 15 cout << "A's a is" << x << endl; 16 } 17 ~A() 18 { 19 cout << "A's destructor" << endl; 20 }
輸出如下:
看到了吧!! (0.0)
其實,我們為了避免拷貝構造函數的使用,我們可以返回A類的引用。這樣就避免了copy構造函數的調用。
//A getA() A& getA() { return a; }
輸出如下: