【c++继承】继承关系中派生类对象构造函数和析构函数调用顺序

时间:2022-09-07 21:24:47

一、派生类对象中基类构造函数和派生类中构造函数调用顺序(WIN7 64位 VS2012)

(1)、先执行派生类的构造函数还是先执行基类中的构造函数?

利用下面这段代码进行打印测试
#include <iostream>
using namespace std;

// 基类
class BaseClass
{
public:
BaseClass()
{
cout << "BaseClass()" <<endl;
}
};

// 派生类
class DrivedClass:public BaseClass
{
public:
DrivedClass()
{
cout << "DrivedClass()" <<endl;
}
};

int main()
{
DrivedClass d1;
return 0;
}



我们来看看打印结果:
【c++继承】继承关系中派生类对象构造函数和析构函数调用顺序
打印出的结果显示,创建派生类对象时先执行基类的构造函数,然后在执行派生类中的构造函数,那么结果是不是这样呢?反汇编走一波。
主函数创建对象时:
【c++继承】继承关系中派生类对象构造函数和析构函数调用顺序

可以看出创建对象时,在取(lea)到对象d1的地址后,调用(call)的是派生的构造函数,然后继续跟进

【c++继承】继承关系中派生类对象构造函数和析构函数调用顺序

进到派生类的构造函数后,我们可以看到,在执行派生类的构造函数的函数体之前,有一条语句去调用了基类的构造函数,这也就解释了为什么在打印的时候先输出的是基类构造函数里的内容。 也就是说执行顺序是:先去调用(call)派生类中的构造函数,在进入函数体之前,先去调用(call)基类的构造函数,基类构造函数执行完毕后,再继续执行派生类中的构造函数,执行完毕对象创建成功。

【c++继承】继承关系中派生类对象构造函数和析构函数调用顺序


(2)、基类构造函数的调用是在派生类构造函数初始化列表之前还是之后呢?

看到这里又有了新的问题,c++的构造函数可以有初始化列表的,那么我们将代码稍作修改后如下,在派生类中加上成员_data,并在派生类构造函数的初始化列表中初始化_data为10,并跟踪调用过程,观察基类的构造函数是在初始化列表之前被调用还是初始化列表之后被调用

【c++继承】继承关系中派生类对象构造函数和析构函数调用顺序
由于我们加入了初始化列表,所以顺序也发生了一些小小的改变, 请注意上图中表示1的位置,与没有初始化列表的时对比发生了变化,此时调用基类的构造函数是在函数体内调用进去函数体后我们可以清楚的看到,_data的初始化是在基类构造函数调用之后才执行的。 【c++继承】继承关系中派生类对象构造函数和析构函数调用顺序
总结:虽然在汇编代码中,是先调用的派生类的构造函数,但是总的来说,基类的构造函数还是先于派生类的构造函数里的语句先执行,也就是说,基类对象先于派生类对象被创建。


二、派生类对象中基类析构函数和派生类析构函数调用顺序

测试代码:
#include <iostream>
using namespace std;

// 基类
class BaseClass
{
public:
BaseClass()
{}

~BaseClass()
{
cout << "~BaseClass()" <<endl;
}

};

// 派生类
class DrivedClass:public BaseClass
{
public:
DrivedClass()
{ }

~DrivedClass()
{
cout << "~DrivedClass()" <<endl;
}
};

int main()
{
DrivedClass d1;
return 0;// 在此处下断点
}

输出结果:
【c++继承】继承关系中派生类对象构造函数和析构函数调用顺序
我们知道对象析构的顺序是与创建的时候是相反的,先创建的最后被释放,后创建的先被释放。一句话就是落后就要挨打(析构)。 那么按照(1)中的总结所说,基类创建是先于派生类的,那么按照落后就要挨打的原则,先释放派生类,再释放基类。
【c++继承】继承关系中派生类对象构造函数和析构函数调用顺序

在下面的汇编代码中可以看出是先执行派生类的析构函数,然后再去执行基类的析构函数。

~DrivedClass()
{
01212EA0 push ebp
01212EA1 mov ebp,esp
01212EA3 push 0FFFFFFFFh
01212EA5 push 1218C08h
01212EAA mov eax,dword ptr fs:[00000000h]
01212EB0 push eax
01212EB1 sub esp,0CCh
01212EB7 push ebx
01212EB8 push esi
01212EB9 push edi
01212EBA push ecx
01212EBB lea edi,[ebp-0D8h]
01212EC1 mov ecx,33h
01212EC6 mov eax,0CCCCCCCCh
01212ECB rep stos dword ptr es:[edi]
01212ECD pop ecx
01212ECE mov eax,dword ptr ds:[0121F0E0h]
01212ED3 xor eax,ebp
01212ED5 push eax
01212ED6 lea eax,[ebp-0Ch]
01212ED9 mov dword ptr fs:[00000000h],eax
01212EDF mov dword ptr [this],ecx
01212EE2 mov dword ptr [ebp-4],0
cout << "~DrivedClass()" <<endl;
01212EE9 mov esi,esp
01212EEB mov eax,dword ptr ds:[0122031Ch]
01212EF0 push eax
01212EF1 push 121CC80h
01212EF6 mov ecx,dword ptr ds:[1220318h]
01212EFC push ecx
01212EFD call std::operator<<<std::char_traits<char> > (012112B2h)
01212F02 add esp,8
01212F05 mov ecx,eax
01212F07 call dword ptr ds:[1220324h]
01212F0D cmp esi,esp
01212F0F call __RTC_CheckEsp (01211334h)
}
01212F14 mov dword ptr [ebp-4],0FFFFFFFFh
01212F1B mov ecx,dword ptr [this]
01212F1E call BaseClass::~BaseClass (012113E8h)
01212F23 mov ecx,dword ptr [ebp-0Ch]
01212F26 mov dword ptr fs:[0],ecx