C++中构造函数和析构函数的调用顺序是什么?使用一些基类和派生类的例子进行说明。
执行顺序如下:
示例:
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派生而来,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 []
行为吗? - franji1我必须补充前面的答案,因为每个人似乎都忽略了它。
当创建派生类实例时,确实会在派生类构造函数内部调用基类构造函数中的代码之前,但要记住,从技术上讲,在基类之前仍然可以创建派生类。
而且,在调用派生类析构函数时,确实会在派生类析构函数内部调用代码之前调用基类析构函数内部的代码,但也要记住,在派生类之前销毁基类。
当我说创建/销毁时,我实际上是指分配/释放。
如果查看这些实例的内存布局,您将看到派生实例组成基实例。例如:
派生类内存:0x00001110 到 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答案中的输出。但是内存中对象的行为实际上不是按照相同顺序进行的,更像是这样:
构造:
分配Derived
分配Base
调用Base构造函数
调用Derived构造函数
析构:
调用Derived析构函数
调用Base析构函数
释放Base
释放Derived
std::string
(非指针)时,字符串也会在构造函数之前分配给组合者。派生类组成基类并将其包含在其内存空间中,因此必须先分配派生类。如果你打印地址,这将更加清楚。甚至可以通过将派生类中的“this”强制转换为基类来访问基类实例,并使用它来执行一些操作。 - Everyone#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