Порядок вызова конструктора и деструктора члена

121

О гуру 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::~

Другими словами, гарантируется ли инициализация членов в порядке объявления и уничтожение в обратном порядке?

СБК
источник
8
Это довольно частая причина мелких ошибок, когда классы стали большими и невыразительными. Когда у вас есть 50 элементов данных, и многие из них инициализированы в списке инициализатора конструктора, можно легко предположить, что порядок построения - это порядок в списке инициализатора. В конце концов, авторы кода тщательно упорядочили список ... не так ли?
Permaquid

Ответы:

140

Другими словами, гарантируется ли инициализация членов в порядке объявления и уничтожение в обратном порядке?

Да обоим. См. 12.6.2

6 Инициализация выполняется в следующем порядке:

  • Во-первых, и только для конструктора наиболее производного класса, как описано ниже, виртуальные базовые классы должны быть инициализированы в том порядке, в котором они появляются при обходе направленного ациклического графа базовых классов в глубину слева направо, где «left -to-right »- это порядок появления имен базовых классов в списке базовых спецификаторов производного класса.

  • Затем прямые базовые классы должны быть инициализированы в порядке объявления, как они появляются в списке-спецификаторах базы (независимо от порядка инициализаторов памяти).

  • Затем нестатические элементы данных должны быть инициализированы в том порядке, в котором они были объявлены в определении класса (опять же, независимо от порядка инициализаторов памяти).

  • Наконец, выполняется составной оператор тела конструктора. [Примечание: порядок объявления необходим, чтобы гарантировать, что базовые и членские подобъекты уничтожаются в порядке, обратном инициализации. - конец примечания]

dirkgently
источник
2
Если я правильно помню, да для обоих ... Думайте об этом как о стеке. Первое нажатие, последнее нажатие. Итак, при создании вашего первого экземпляра он помещается в память в порядке стека. Затем второй сдвигается, третий - на второй и так далее. Затем, уничтожая ваши экземпляры, программа будет искать первый, чтобы уничтожить, последний, который был отправлен. Но, возможно, я ошибаюсь, объясняя это таким образом, но именно так я узнал, когда делал C / C ++ и ASM.
Уилл Маркуиллер,
29

Да, они есть (то есть нестатические члены). См. 12.6.2 / 5 для инициализации (построения) и 12.4 / 6 для разрушения.

Муравей
источник
10

Да, стандарт гарантирует, что объекты будут уничтожены в порядке, обратном их созданию. Причина в том, что один объект может использовать другой и зависеть от него. Рассматривать:

struct A { };

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

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

Если бы он aбыл уничтожен раньше, bто bсодержал бы недопустимую ссылку на член. Уничтожая объекты в порядке, обратном их созданию, мы гарантируем правильное разрушение.

wilhelmtell
источник
На самом деле я никогда не думал, что это правило применимо и к порядку уничтожения членов области видимости!
yano
6

Да и да. Порядок уничтожения всегда противоположен порядку построения для переменных-членов.

Крис Джестер-Янг
источник