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


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 Ініціалізація проходить у такому порядку:

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

  • Потім прямі базові класи ініціалізуються в порядку декларування, коли вони відображаються у списку базових специфікаторів (незалежно від порядку ініціалізаторів пам’яті).

  • Потім нестатичні члени даних ініціалізуються в тому порядку, який вони були оголошені у визначенні класу (знову ж таки незалежно від порядку пам’яті ініціалізаторів).

  • Нарешті, виконується з'єднання-заява тіла конструктора. [Примітка: наказ декларації призначений для того, щоб знищити суб'єкти бази та членів у зворотному порядку ініціалізації. —Закінчити примітку]


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мали б недійсну посилання на члена. Знищуючи об'єкти в зворотному порядку, в якому вони були створені, ми гарантуємо правильне знищення.


Я ніколи фактично не вважав, що це правило застосовується і до порядку знищення членів сфери застосування!
яно

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.