本人在学习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; }
输出如下: