成员构造函数和析构函数调用的顺序

C + + 大师,我寻求你的智慧。用标准语言告诉我 C + + 是否能保证下面的程序:

#include <iostream>
using namespace std;


struct A
{
A() { cout << "A::A" << endl; }
~A() { cout << "A::~" << endl; }
};


struct B
{
B() { cout << "B::B" << endl; }
~B() { cout << "B::~" << endl; }
};


struct C
{
C() { cout << "C::C" << endl; }
~C() { cout << "C::~" << endl; }
};


struct Aggregate
{
A a;
B b;
C c;
};


int main()
{
Aggregate a;
return 0;
}

将永远产生

A::A
B::B
C::C
C::~
B::~
A::~

换句话说,是否保证按声明的顺序初始化成员,并按相反的顺序销毁成员?

66418 次浏览

对于成员变量来说,破坏的顺序总是与结构的顺序相反的。

是的,它们是(即非静态成员)。初始化(构造)参见12.6.2/5,销毁参见12.4/6。

换句话说,是否保证按声明的顺序初始化成员,并按相反的顺序销毁成员?

两者都是,见12.6.2

初始化将在 命令如下:

  • 第一次,也是唯一一次 派生最多的构造函数 类,虚拟基 初始化类 它们出现的顺序是深度优先 从左到右遍历 有向无环图 类,其中“从左到右”是 基类的外观顺序 派生类中的名称 基本-说明符-列表。

  • 那就直接说 基类应在 申报令,因为他们出现在 基本说明符列表(不管 Mem 初始化器的顺序)。

  • 然后,非静态数据成员应为 按顺序初始化 在类定义中声明 (同样不管 Mem- 初始化器)。

  • 最后 构造函数的复合语句 主体被执行 声明令的授权 确保基础和成员子对象 以相反的顺序被摧毁 初始化ーー结束注释]

是的,标准保证对象按照创建时的相反顺序被销毁。原因是一个对象可以使用另一个,因此取决于它。考虑一下:

struct A { };


struct B {
A &a;
B(A& a) : a(a) { }
};


int main() {
A a;
B b(a);
}

如果 ab之前被破坏,那么 b将持有一个无效的成员引用。通过按照创造物体的相反顺序毁灭它们,我们保证了正确的毁灭。

关于析构函数,下面是标准的第12.4.8段,它证明了第二个“是”:

执行析构函数体并销毁在该体中分配的任何自动对象之后, 类的析构函数调用 X 的直接非变量非静态数据成员的析构函数,即析构函数 对于 X 的直接基类,如果 X 是最派生类(12.6.2)的类型,则其析构函数调用 所有析构函数都被调用,就好像它们被一个限定的 名称,即忽略更多派生类中任何可能的虚重写析构函数 成员被销毁的顺序与其构造函数的完成顺序相反(见12.6.2) 语句(6.6.3)在析构函数中可能不会直接返回给调用方; 在将控制权转移到 调用方,则调用成员和基的析构函数 按照它们的结构顺序排列(见12.6)。

但是请注意,容器通常不跟踪内容的时间顺序,因此它们可能以非直观的方式表现。例如,std: : Vector 可能会从头到尾销毁它的对象,尽管事实上它们通常是用 push _ back ()或类似的东西填充的。 因此,不能通过容器实现 ctor-dtor 堆栈。

这是我的小插图:

#include <vector>
#include <stdio.h>


struct c
{
c() : num(++count) { fprintf(stderr, "ctor[%u], ", num);}
~c(){ fprintf(stderr, "dtor[%u], ", num);}
private:
static unsigned count;
unsigned num;
};
unsigned c::count = 0;
int main()
{
std::vector<c> v(5);
}

然后我得到了: Ctor [1] ,ctor [2] ,ctor [3] ,ctor [4] ,ctor [5] ,dtor [1] ,dtor [2] ,dtor [3] ,dtor [4] ,dtor [5] ,