[英]Destruction of objects with static storage duration
考慮以下程序。
struct s { ~s(); };
void f()
{
static s a;
}
struct t { ~t() { f(); } };
int main()
{
static s b;
static t c;
}
我試圖找出關於靜態對象破壞的標准保證是什么,但我發現C ++ 03 [basic.start.term]的文本相當不足。
是否定義了程序的行為? 如果是這樣,靜態對象a
, b
和c
的破壞順序是什么? 如果s::~s
〜s引發異常會怎么樣? 請解釋您的推理,最好使用標准中的引號。
在以下Objects
引用靜態存儲持續時間的對象。
全局命名空間中的對象在main之前創建。
根據定義創建同一編譯單元中的對象。
訂單在不同的編譯單元中未定義。
命名空間中的對象是在訪問該命名空間中的任何函數/變量之前創建的。 這可能是也可能不是在主要之前。
函數中的對象在首次使用時創建。
對象以創建的相反順序銷毀。 請注意,創建順序由其CONSTRUCTOR的完成定義(而不是在調用時)。 因此,在其構造函數中創建另一個'y'的一個對象'x'將導致首先構造'y'。
如果它們沒有被創建,那么它們將不會被銷毀。
是否定義了程序的行為?
所以是的,訂單定義明確
b: Created
c: Created
c: Destroyed Start
a: Created (during destruction of C)
c: Destroyed End
a: Destroyed (a was created after b -> destroyed before b)
b: Destroyed
修改代碼以查看:
#include <iostream>
struct s
{
int mx;
s(int x): mx(x) {std::cout << "S(" << mx << ")\n";}
~s() {std::cout << "~S(" << mx << ")\n";}
};
void f()
{
static s a(3);
}
struct t
{
int mx;
t(int x): mx(x) { std::cout << "T(" << mx << ")\n";}
~t()
{ std::cout << "START ~T(" << mx << ")\n";
f();
std::cout << "END ~T(" << mx << ")\n";
}
};
int main()
{
static s b(1);
static t c(2);
}
輸出是:
$ ./a.exe
S(1)
T(2)
Start ~T(2)
S(3)
END ~T(2)
~S(3)
~S(1)
如前所述,析構函數調用的順序與構造函數的完成順序完全相反( 3.6.3/1
)。 換句話說( 3.8/1
),靜態存儲持續時間對象的生命周期停止與靜態存儲持續時間對象的生命周期開始相反。 所以這一切都歸結為他們的構造函數被調用。 假設Printer
是在以下示例中在其構造函數中輸出某些內容的類型。
在第一次使用在( 3.6.2/3
)中定義對象的相同轉換單元中定義的任何函數或變量之前,命名空間范圍(全局和用戶定義)的對象在任何情況下都是創建的。 這個延遲初始化(在調用main之后)必須遵守該對象定義的相同轉換單元中關於其他對象定義的定義順序。 ( 3.6.2/1
)。
翻譯單位1 :
void f() { }
extern Printer a;
Printer b("b");
Printer a("a");
extern Printer c;
翻譯單位2:
Printer c("c");
void f();
如果我們使用f
,這不一定會強制創建c
,因為F不是在翻譯單元定義c
在定義。 a
是后創建b
,因為它后面定義。
當控件首先通過其定義或首次為POD輸入塊時( 6.7/4
),將創建塊范圍(局部靜態)對象。 如果創建無法成功(如果發生異常)( 6.7/4
),則下次控制傳遞時會再次嘗試啟動生命周期。
void g() { static Print p("P"); }
struct A {
A() {
static int n;
if(n++ == 0) throw "X";
cout << "A";
g();
}
};
void f() {
try { static A a; } catch(char const *x) { cout << x; }
}
此代碼段輸出“XAP”。
對於靜態數據成員,相同的規則適用於在同一翻譯單元( 3.6.2/1
)內根據其定義順序的初始化順序。 這是因為它的規則被公式化為“在命名空間范圍中定義的對象......”而不是“命名空間范圍的對象......”。 在C ++ 03中,延遲初始化(延遲構造直到從其轉換單元使用變量/函數)僅允許用於命名空間范圍的對象, 這不是預期的 。 C ++ 0x也允許靜態數據成員(“具有靜態存儲持續時間的非本地變量”)。
因此,通過采用上述規則並考慮到破壞順序實際上是由構造函數的完成而不是從它們的開始確定的,我們將得到命令~c
~a
~b
。
如果s::~s
拋出一個異常,C ++ 0x中說, terminate()
被調用,並且最終不得不c
破壞和已經結束的一生a
沒有完成它的析構函數,如果它拋出異常。 我在C ++ 03標准中找不到任何指定的內容。 它似乎只指定對於非局部靜態而不是像C ++ 0x那樣的塊范圍靜態。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.