10_2、C++继承与派生:派生类的构造和析构函数

派生类的构造函数

基类的构造函数和析构函数派生类是不能继承的。如果派生类需要对新成员初始化或者进行特定的清理工作,就需要自己定义构造函数和析构函数了。从基类继承的成员的初始化仍可通过基类的构造函数来完成。
派生类的数据成员:

  1. 从基类继承来的数据成员;
  2. 派生类新增的数据成员;
  3. 其他类的对象作为其数据成员,包括其他类的对象时实际上还间接包括了这些对象的数据成员。

对派生类初始化时就需要对基类的数据成员、派生类新增数据成员和内嵌的其他类对象的数据成员进行初始化。
派生类构造函数的语法形式为:

派生类名::派生类名(参数表):基类名1(参数表1),...基类名m(参数名m),内嵌对象名(内嵌对象参数表1),...,内嵌对象名n(内嵌对象参数表n)
{
    初始化派生类新成员的语句;
}
  • 派生类的构造函数需要做的工作有:使用传递给派生类的参数,调用基类的构造函数和内嵌对象成员的构造函数来初始化它们的数据成员,再添加新语句初始化派生类新成员。
  • 构造函数参数表要给出初始化基类数据成员、新增数据成员和内嵌对象的数据成员的所有参数。所有这些参数以后就要指明所有要初始化的基类名及其参数表,还有内嵌对象名及其参数表。
  • 各个基类名和内嵌对象名可以以任何的顺序排列。
  • 基类的构造函数若有参数,则派生类必须定义构造函数,将传入的参数再传递给基类的构造函数,对基类进行初始化。

假设一个类继承自多个基类,即为多继承时,对于那些构造函数有参数的基类就必须显式给出基类名及其参数表,而对于那些使用默认构造函数的基类没有必要给出基类名及其参数表,同理内嵌对象的构造函数若有参数也必须给出内嵌对象名及其参数表,若使用默认构造函数也没有必要给出内嵌对象名及其参数表。试想,如果派生类只有一个基类,而且有默认构造函数,没有内嵌对象或者可以使用其他公有函数成员初始化内嵌对象,那么后面的基类名和内嵌对象名就都不需要了,是不是形式就很简单了?其实我们最常用的比这种最简单的情况复杂不了多少。

派生类构造函数处理顺序

构造派生类的对象调用构造函数时的处理顺序是:

  1. 首先调用基类的构造函数,若有多个基类,调用顺序按照它们在派生类声明时从左到右出现的顺序;
  2. 如果有内嵌对象成员,则调用内嵌对象成员的构造函数,若为多个内嵌对象,则按照它们在派生类中声明的顺序调用,如果无内嵌对象则跳过这一步;
  3. 调用派生类构造函数中的语句。
  4. 基类和内嵌对象成员的构造函数的调用顺序和它们在派生类构造函数中出现的顺序无关。
#include <iostream>
using namespace std;
class Base1          // 基类Base1,只有默认构造函数
{
public:
      Base1()         { cout<<"Base1 construct"<<endl; }
};
class Base2          // 基类Base2,只有带参数的构造函数
{
public:
    Base2(int x)    { cout<<"Base2 construct "<<x<<endl; }
};
class Base3          // 基类Base3,只有带参数的构造函数
{
public:
   Base3(int y)    { cout<<"Base3 construct "<<y<<endl; }
};
class Child : public Base2, public Base1, public Base3   // 派生类Child  派生声明
{
public:
     Child(int i,int j,int k,int m):Base2(i),b3(j),b2(k),Base3(m)    { }
private:             // 派生类的内嵌对象成员,声明顺序
     Base1 b1;
     Base2 b2;
     Base3 b3;
};
int main()
{
    Child child(3,4,5,6);
    return 0;
}

在这里插入图片描述

派生类的析造函数

派生类无法继承基类的析构函数,所以如果需要的话就要自己定义析构函数。派生类析构函数的定义方式与一般类的析构函数是一样的,也是没有返回类型,没有参数。
派生类的析构函数一般只需要在其函数体中清理新增成员就可以了,对于继承的基类成员和派生类内嵌对象成员的清理,则一般由系统自动调用基类和对象成员的析构函数来完成。这个执行过程的顺序正好和派生类构造函数相反:

  1. 执行析构函数语句清理派生类的新增成员;
  2. 调用内嵌对象成员所属类的析构函数清理派生类内嵌对象成员,各个对象成员的清理顺序与其在构造函数中的构造顺序相反;
  3. 调用基类的析构函数清理继承的基类成员,如果是多继承则各个基类的清理顺序也与其在构造函数中的构造顺序相反。
    总起来一句话,析构函数执行时所有成员或对象的清理顺序与构造函数的构造顺序刚好完全相反。
#include <iostream>       
using namespace std;
class Base1          // 基类Base1
{
public:
     Base1()         { cout<<"Base1 construct"<<endl; }       // Base1的构造函数
     ~Base1()        { cout<<"Base1 destruct"<<endl; }        // Base1的析构函数
};
class Base2          // 基类Base2
{
public:
    Base2(int x)    { cout<<"Base2 construct "<<x<<endl; }    // Base2的构造函数
    ~Base2()        { cout<<"Base2 destruct"<<endl; }         // Base2的析构函数
};
class Base3          // 基类Base3
{
public:
    Base3(int y)    { cout<<"Base3 construct "<<y<<endl; }     // Base3的构造函数
    ~Base3()        { cout<<"Base3 destruct"<<endl;}           // Base3的析构函数
};
class Child : public Base2, public Base1, public Base3   // 派生类Child
{
public:
    Child(int i,int j,int k,int m):Base2(i),b3(j),b2(k),Base3(m)    { }
private:             // 派生类的内嵌对象成员
    Base1 b1;
    Base2 b2;
    Base3 b3;
};
int main()
{
    Child child(3,4,5,6);
    return 0;
}
  • 三个基类Base1、Base2和Base3都添加了析构函数,派生类Child没有添加,系统会为派生类生成默认析构函数。主函数的函数体没有变。程序执行时会先调用Child类的构造函数构造child对象,然后调用Child类的默认析构函数完成清理工作。

运行结果:析构函数的执行顺序与构造函数完全相反。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值