构造函数和析构函数
派生类的构造函数和析构函数的构造是讨论的主要问题,读者要掌握它。
1. 构造函数
我们已知道,派生类的对象的数据结构是由基类中说明的数据成员和派生类中说明的数据成员共同构成。将派生类的对象中由基类中说明的数据成员和操作所构成的封装体称为基类子对象,它由基类中的构造函数进行初始化。
构造函数不能够被继承,因此,派生类的构造函数必须通过调用基类的构造函数来初始化基类子对象。所以,在定义派生类的构造函数时除了对自己的数据成员进行初始化外,还必须负责调用基类构造函数使基类数据成员得以初始化。如果派生类中还有子对象时,还应包含对子对象初始化的构造函数。
派生类构造函数的一般格式如下:
<派生类名>(<派生类构造函数总参数表>):<基类构造函数>(参数表1),<子对象名>(<参数表2>) { <派生类中数据成员初始化> };
派生类构造函数的调用顺序如下:
· 基类的构造函数
· 子对象类的构造函数(如果有的话)
· 派生类构造函数
在前面的例子中,B::B(int i, int j, int k):A(i), bb(j), bbb(k)就是派生类构造函数的定义,下面再举一个构造派生类构造函数的例子。
2. 构造函数
当对象被删除时,派生类的析构函数被执行。由于析构函数也不能被继承,因此在执行派生类的析构函数时,基类的析构函数也将被调用。执行顺序是先执行派生类的构造函数,再执行基类的析构函数,其顺序与执行构造函数时的顺序正好相反。这一点从前面讲过的例子可以看出,请读者自行分析。
3. 派生类构造函数使用中应注意的问题
(1) 派生类构造函数的定义中可以省略对基类构造函数的调用,其条件是在基类中必须有缺省的构造函数或者根本没有定义构造函数。当然,基类中没有定义构造函数,派生类根本不必负责调用基类的析构函数。
(2) 当基类的构造函数使用一个或多个参数时,则派生类必须定义构造函数,提供将参数传递给基类构造函数途径。在有的情况下,派生类构造函数的函数体可能为空,仅起到参数传递作用。如本讲第一个例子就属此种情况。
子类型化和类型适应
1. 子类型化
子类型的概念涉及到行为共享,它与继承有着密切关系。
有一个特定的类型S,当且仅当它至少提供了类型T的行为,由称类型S是类型T的子类型。子类型是类型之间的一般和特殊的关系。
在继承中,公有继承可以实现子类型。例如:
class A { public: void Print() const { cout<<"A::print() called./n"; } }; class B : public A { public: void f() {} };
|
类B继承了类A,并且是公有继承方式。因此,可以说类B是类A的一个子类型。类A还可以有其他的子类型。类B是类A的子类型,类B具备类A中的操作,或者说类A中的操作可被用于操作类B的对象。
子类型关系是不可逆的。这就是说,已知B是A的子类型,而认为A也是B的子类型是错误的,或者说,子类型关系是不对称不。
因此,可以说公有继承可以实现子类型化。
2. 类型适应
类型适应是指两种类型之间的关系。例如,B类型适应A类型是指B类型的对象能够用于A类型的对象所能使用的场合。
前面讲过的派生类的对象可以用于基类对象所能使用的场合,我们说派生类适应于基类。
同样道理,派生类对象的指针和引用也适应于基类对象的指针和引用。
子类型化与类型适应是致的。A类型是B类型的子类型,那么A类型必将适应于B类型。
子类型的重要性就在于减轻程序人员编写程序代码的负担。因为一个函数可以用于某类型的对象,则它也可以用于该类型的各个子类型的对象,这样就不必为处理这些子类型的对象去重载该函数。 
2/2 首页 上一页 1 2 |