[英]Order of member constructor and destructor calls
哦,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::~
換句話說,成員是否保證按聲明順序初始化並按相反順序銷毀?
換句話說,成員是否保證按聲明順序初始化並按相反順序銷毀?
兩者都是。 見 12.6.2
6初始化應按以下順序進行:
首先,並且僅對於如下所述的最派生類的構造函數,虛擬基類應按照它們在基類的有向無環圖的深度優先從左到右遍歷中出現的順序進行初始化,其中“左-to-right”是派生類基說明符列表中基類名稱的出現順序。
然后,直接基類應按照它們出現在 base-specifier-list 中的聲明順序進行初始化(無論 mem-initializer 的順序如何)。
然后,非靜態數據成員應按照它們在類定義中聲明的順序進行初始化(同樣不管 mem-initializers 的順序)。
最后,執行構造函數主體的復合語句。 [注意:聲明順序是為了確保基子對象和成員子對象以初始化的相反順序被銷毀。 ——尾注]
是的,它們是(即非靜態成員)。 初始化(構造)見 12.6.2/5,銷毀見 12.4/6。
是的,標准保證對象按照它們創建的相反順序被破壞。 原因是一個對象可能使用另一個對象,因此依賴它。 考慮:
struct A { };
struct B {
A &a;
B(A& a) : a(a) { }
};
int main() {
A a;
B b(a);
}
如果a
在b
之前銷毀,則b
將持有無效的成員引用。 通過以與創建對象相反的順序銷毀對象,我們保證了正確的銷毀。
是的,是的。 對於成員變量,銷毀的順序總是與構造的順序相反。
關於析構函數。 這是標准的第 12.4.8 段,它證明了第二個“是”:
在執行析構函數的主體並銷毀主體內分配的任何自動對象后,類 X 的析構函數調用 X 的直接非變體非靜態數據成員的析構函數,X 的直接基類的析構函數,如果 X 是最派生類 (12.6.2) 的類型,它的析構函數調用 X 的虛擬基類的析構函數。 所有的析構函數都被調用,就好像它們被一個限定名引用一樣,也就是說,忽略更多派生類中任何可能的虛擬覆蓋析構函數。 基和成員按照其構造函數完成的相反順序被銷毀(見 12.6.2)。 析構函數中的 return 語句 (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],
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.