组合
在面向对象程序设计中,可以对复杂对象进行分解、抽象,把一个复杂对象分解为简单对象的组合,由比较容易理解和实现的部件对象装配而成。
实际上类的成员数据既可以是基本类型也可以是自定义类型,当然也可以是类的对象。
类的组合描述的就是一个类内嵌其他类的对象作为成员的情况,他们之间的关系是一种包含与被包含的关系。
当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动创建。因此,在创建对象时既要对本类的基本类型数据成员进行初始化,又要对内嵌对象成员进行初始化。这时,理解这些对象的构造函数被调用的顺序就很重要了。
组合类构造函数定义的一般形式为:
类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表),...
{类的初始化}
其中,内嵌对象1(形参表),内嵌对象2(形参表),...称作初始化列表,其作用是对内嵌对象进行初始化。
对基本类型的数据成员也可以这样初始化,而且使用初始化列表比使用赋值语句的效率要高。如
Circle::Circle(float r):radius(r){}
在创建一个组合类的对象时,不仅它自身的构造函数将被调用,而且还将调用内嵌对象的构造函数。这时构造函数的调用顺序是:
a、调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的定义中出现的次序。
b、执行本类构造函数的函数体。
如果声明组合类的对象时没有指定对象的初始值,则默认形式(无形参)的构造函数被调用,这时内嵌对象的默认形式构造函数也被调用。析构函数的调用执行顺序与构造函数刚好相反。
对于一个类,如果没有编写拷贝拷贝构造函数,编译系统会在必要时自动生成一个默认的拷贝构造函数。若建立组合类的对象时调用默认拷贝构造函数,则编译器将自动调用内嵌成员对象的拷贝构造函数。
如果要为组合类编写拷贝构造函数,则需要为内嵌成员对象的拷贝构造函数传递参数,如假设C类中包含B类的对象b作为成员,C类的拷贝构造函数形式如下:
C::C(C &c1):b(c1.b)
{...}
例子:
类的组合,线段类,由两个Point类的对象来表示端点,使用类line包括Point类的两个对象p1和p2作为其成员,Line具有计算其长度的功能,在构造函数中实现。
2、前向引用声明
考虑到类的组合中,很可能遇到两个类相互引用的情况,这种情况也称为循环依赖,因为不管把谁放在前面都会存在未定义先使用的情况,故引入了前向引用,它是在引用未定义的类之前,将该类的名字告诉编译器,使编译器知道那是一个类名。这样,当程序中使用这个类名时,编译器就不会认为是错误,而类的完整定义可以在程序的其他地方。
应该记住,当使用前向引用声明时,只能使用被声明的符号,而不能涉及类的任何细节。