C++超复杂的构造和析构函数执行顺序详解

几个类的继承关系

假设有如下代码:

class A
{
public:
    A(){cout<<"A constructed"<<endl;}
    ~A(){cout<<"A delete"<<endl;}
};

class B:public A
{
public:
    B(int v=1):p(v){cout<<"B constructed"<<endl;}
    ~B(){cout<<"B delete"<<endl;}

    int p;
};

class C:public B
{
private:
    B b;
public:
    C(){cout<<"C constructed"<<endl;}
    C(B x):b(x){cout<<"C 的B被赋值"<<endl;}
    ~C(){cout<<"C delete"<<endl;}
};

一重继承示例: B b1;

现在有如下变量创建:

B b1;

我们来看看结果:

A constructed
B constructed
B delete
A delete

这是为什么呢?

1、构造函数的调用顺序:自上而下
当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的构造函数,依次类推,直至到达最底层目标派生类的构造函数。

也就是说,不过基类有没有成员,都会执行它的构造函数。所以开始是A constructed,其次执行B类自己的构造函数,即:B constructed.

2、析构函数的调用顺序:自下而上
当删除一个对象时,首先调用该派生类的析构函数,然后调用上一层基类的析构函数,依次类推,直到到达最顶层的基类析构函数

故而,删除的时候,从b1这个变量自己开始调用析构函数,即B delete,在调用基类的析构函数,即A delete.

二重继承示例:C c;

代码如下:

    C c;

结果是:

A constructed
B constructed
A constructed
B constructed
C constructed
C delete
B delete
A delete
B delete
A delete

这是为什么呢?

首先一点,成员初始化的次序是类定义中声明成员的次序

也就是说,先按声明的顺序,对当前类C的成员变量进行初始化,而当前C的成员变量只有一个即: B b; 由上面的示例我们知道,初始化它的时候,的构造函数的执行顺序是:

A constructed
B constructed

其次,我们需要构造变量C c本身,同样根据上面的示例,知道是:

A constructed
B constructed
C constructed

由此构造函数调用完毕。

那么析构函数呢,则是跟构造函数相反的方向。先对c变量析构,所以有:

C delete
B delete
A delete

其次对c变量的成员变量 b进行析构,也就是:

B delete
A delete

由此,执行顺序搞定。

带参数的负责构造: C c(b1);

假设代码有:

 B b1;
 C c(b1);

那么其结果是:

A constructed
B constructed
A constructed
B constructed
C 的B被赋值
B delete
A delete
C delete
B delete
A delete
B delete
A delete
B delete
A delete

咋们来捋一捋:
首先创建 B b1;对象,有:

A constructed
B constructed

其次,创建 C c(b1);由于带参数,所以不用默认构造函数了,执行带参数的构造函数,所以有:

A constructed
B constructed
C 的B被赋值

来看析构函数,析构函数的执行顺序是,最后一个类变量(这里 是c)先析构,然后在析构b变量;其实就是按在程序当中声明的反顺序来的。

故而这里先析构c变量:

C delete
B delete
A delete

但是我们发现程序运行结果不是这样,这是为什么呢?

这是因为需要先析构 C的构造函数当中C(B x) 的B x这个临时变量,所以先是:

B delete
A delete

再是c变量自己:

C delete
B delete
A delete

再是 c变量里面的类成员 b:

B delete
A delete

最后是主程序中的 B b1变量的析构调用:

B delete
A delete

由此析构函数执行完毕。

默认构造函数带缺省值

假设在B类中,有:

B(int v=1):p(v){cout<<"B constructed"<<endl;}

那么当主程序中创建变量时:

B b1;

将会执行上面那个构造函数,所以b1.p=1(为了方便,这里对p使用了公开)。

如果同时写:

B(){cout<<"B constructed"<<endl;}
B(int v=1):p(v){cout<<"B 缺省constructed"<<endl;}

这样的话,编译是错误的,因为两个构造函数,导致编译器不知道调用哪个。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值