继承和组合
组合:
简单地创建一个包含已存在的类对象的新类,这称为组合,因为这个新类是由已存在类的对象组合的。
继承:
创建一个新类作为一个已存在类的类型,采取这个已存在类的形式,对它增加代码,但不修改它。这个有趣的活动被称为继承,其中大量的工作由编译器完成。继承是面向对象程序设计的基石
构造函数和析构函数的次序:
当一个对象有许多子对象时,知道构造函数和析构函数的调用次序是有趣的。下面的例子明显表明它如何工作:
#include<fstream>
usingnamespace std;
ofstreamout("testinitorder.out");
#defineCLASS(CLASSNAME) class CLASSNAME { \
public:\
CLASSNAME(int){out << #CLASSNAME " constructor\n";} \
~CLASSNAME() {out << #CLASSNAME " destructor\n";} \
};
CLASS(base1);
CLASS(member1);
CLASS(member2);
CLASS(member3);
CLASS(member4);
classderived1 : public base1 {
member1m1;
member2m2;
public:
derived1(int): m2(1), m1(2), base1(3) {
out<< "derived1 constructor\n";
}
~derived1(){
out<<"derived1destructor\n";
}
};
classderived2 : public derived1 {
member3m3;
member4m4;
public:
derived2(): m3(1), derived1(2), m4(3){
out<< "derived2 constructor\n";
}
~derived2(){
out<< "derived2 destructor\n";
}
};
intmain()
{
derived2d2;
return1;
}
上面这段程序的输出结果如下:
base1constructor
member1constructor
member2constructor
derived1constructor
member3constructor
member4constructor
derived2constructor
derived2destructor
member4destructor
member3destructor
derived1destructor
member2destructor
member1destructor
base1destructor
可以看出,构造在类层次的最根处开始,而在每一层,首先调用基类构造函数,然后调用
成员对象构造函数。调用析构函数则严格按照构造函数相反的次序—这是很重要的,因为要
考虑潜在的相关性。另一有趣的是,对于成员对象,构造函数调用的次序完全不受在构造函数
的初始化表达式表中次序的影响。该次序是由成员对象在类中声明的次序所决定的。如果能通
过构造函数的初始化表达式表改变构造函数调用次序,那么就会对两个不同的构造函数有二种
不同的调用顺序。而析构函数不可能知道如何为析构函数相应地反转调用次序,这就引起了相
关性问题。