转载自:http://www.jizhuomi.com/software/53.html
类的组合其实描述的就是在一个类里内嵌了其他类的对象作为成员的情况,它们之间的关系是一种包含与被包含的关系。简单说,一个类中有若干数据成员是其他类的对象。以前的教程中我们看到的类的数据成员都是基本数据类型的或自定义数据类型的,比如int、float类型的或结构体类型的,现在我们知道了,数据成员也可以是类类型的。
如果在一个类中内嵌了其他类的对象,那么创建这个类的对象时,其中的内嵌对象也会被自动创建。因为内嵌对象是组合类的对象的一部分,所以在构造组合类的对象时不但要对基本数据类型的成员进行初始化,还要对内嵌对象成员进行初始化。
类组合的构造函数调用
构造函数调用的顺序:先调用内嵌对象的构造函数(按内嵌时类的声明顺序,先声明者先构造)。然后调用本类的构造函数。
(析构函数的调用顺序相反)
若调用缺省构造函数(即无形参),则内嵌对象的初始化也将调用相应的缺省构造函数。
其中,“内嵌对象1(形参表),内嵌对象2(形参表),...”成为初始化列表,可以用于完成对内嵌对象的初始化。其实,一般的数据成员也可以这样初始化,就是把这里的内嵌对象都换成一般的数据成员,后面的形参表换成用来的初始化一般数据成员的变量形参,比如,Point::Point(int xx, int yy):X(xx),Y(yy) { },这个定义应该怎么理解呢?就是我们在构造Point类的对象时传入实参初始化xx和yy,然后用xx的值初始化Point类的数据成员X,用yy的值初始化数据成员Y。
组合类构造函数定义(注意不是声明)的一般形式为:
类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表),...
{
类的初始化
}
声明一个组合类的对象时,不仅它自身的构造函数会被调用,还会调用其内嵌对象的构造函数。那么,这些构造函数的调用是什么顺序呢? 首先,根据前面说的初始化列表,按照内嵌对象在组合类的声明中出现的次序,依次调用内嵌对象的构造函数,然后再执行本类的构造函数的函数体。 比如下面例子中对于Distance类中的p1和p2就是先调用p1的构造函数,再调用p2的构造函数。因为Point p1,p2;是先声明的p1后声明的p2。最后才是执行Distance构造函数的函数体。
这段程序的运行结果是:#include <iostream> #include <tchar.h> 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类的对象,然后构造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是一个类,它具有类的所有属性,具体的定义在其他地方。