【发布时间】:2010-10-13 20:33:11
【问题描述】:
C++ 中调用析构函数和构造函数的顺序是什么?使用一些基类和派生类的例子
【问题讨论】:
标签: c++ constructor destructor
C++ 中调用析构函数和构造函数的顺序是什么?使用一些基类和派生类的例子
【问题讨论】:
标签: c++ constructor destructor
顺序是:
示例:
class B
{
public:
B()
{
cout<<"Construct B"<<endl;
}
virtual ~B()
{
cout<<"Destruct B"<<endl;
}
};
class D : public B
{
public:
D()
{
cout<<"Construct D"<<endl;
}
virtual ~D()
{
cout<<"Destruct D"<<endl;
}
};
int main(int argc, char **argv)
{
D d;
return 0;
}
示例输出:
构造B
构造D
破坏D
破坏B
多层继承就像一个堆栈:
如果您考虑将一个项目压入堆栈作为构造,并将其取出作为破坏,那么您可以将多个层次的继承视为堆栈。
这适用于任意数量的级别。
示例 D2 派生自 D 派生自 B。
将 B 压入堆栈,将 D 压入堆栈,将 D2 压入堆栈。所以施工顺序是B、D、D2。然后找出破坏顺序开始弹出。 D2、D、B
更复杂的例子:
更复杂的例子请看@JaredPar提供的链接
【讨论】:
C++ FAQ Lite 中提供了这些事件的详细描述,包括虚拟继承和多重继承。第 25.14 和 25.15 节
https://isocpp.org/wiki/faq/multiple-inheritance#mi-vi-ctor-order
【讨论】:
另外,请记住,虽然数组元素是先构造的 -> 最后一个,但它们会以相反的顺序被破坏:最后一个 -> 第一。
【讨论】:
new [] / delete [] 行为吗?
我必须补充以前的答案,因为每个人似乎都忽略了它
当你有一个派生类实例被创建时,确实在构造函数中的代码base 的 before 将在 inside 派生 的构造函数中调用,但请记住,派生在技术上仍然是“创建”在基础之前。
当你调用派生类析构函数时,确实内部的代码调用派生析构函数在代码在基析构函数之前,但也要记住base被销毁 在派生之前。
当我说created/destroyed时,我实际上指的是allocated/deallocated。
如果您查看这些实例的内存布局,您会发现派生实例组成基本实例。例如:
派生的内存:0x00001110 to 0x00001120
基础内存:0x00001114 到 0x00001118
因此,派生类必须在构造中在基础之前分配。并且派生类必须在销毁的基础之后被释放。
如果你有以下代码:
class Base
{
public:
Base()
{
std::cout << "\n Base created";
}
virtual ~Base()
{
std::cout << "\n Base destroyed";
}
}
class Derived : public Base
{
public:
Derived()
// Derived is allocated here
// then Base constructor is called to allocate base and prepare it
{
std::cout << "\n Derived created";
}
~Derived()
{
std::cout << "\n Derived destroyed";
}
// Base destructor is called here
// then Derived is deallocated
}
因此,如果您创建了 Derived d; 并使其超出范围,那么您将在 @Brian 的答案中获得输出。但是内存中的对象行为并不是真正的顺序,更像是这样:
建筑:
派生分配
基数分配
调用基础构造函数
调用的派生构造函数
破坏:
调用的派生析构函数
基析构函数调用
基址解除分配
派生解除分配
【讨论】:
std::string(不是指针)时,字符串也在作曲家之后分配,但其构造函数在之前调用。派生类组成基类并将其包含在其内存空间中,因此必须首先分配派生类。如果您打印地址,这将更清楚。您甚至可以通过将派生的this 转换为基实例来访问基实例,并使用它来执行一些操作。
order-dtors-for-members 对此进行了明确描述。基本上,规则是“先构建,后销毁”。
构造函数调用顺序:
析构函数的调用顺序与构造函数的调用顺序相反。
例子:
#include <iostream>
struct base0 { base0(){printf("%s\n", __func__);};~base0(){printf("%s\n", __func__);}; };
struct base1 { base1(){printf("%s\n", __func__);}; ~base1(){printf("%s\n", __func__);};};
struct member0 { member0(){printf("%s\n", __func__);}; ~member0(){printf("%s\n", __func__);};};
struct member1 { member1(){printf("%s\n", __func__);}; ~member1(){printf("%s\n", __func__);};};
struct local0 { local0(){printf("%s\n", __func__);}; ~local0(){printf("%s\n", __func__);}; };
struct local1 { local1(){printf("%s\n", __func__);}; ~local1(){printf("%s\n", __func__);};};
struct derived: base0, base1
{
member0 m0_;
member1 m1_;
derived()
{
printf("%s\n", __func__);
local0 l0;
local1 l1;
}
~derived(){printf("%s\n", __func__);};
};
int main()
{
derived d;
}
输出:
base0
base1
member0
member1
derived
local0
local1
~local1
~local0
~derived
~member1
~member0
~base1
~base0
【讨论】: