static initialization order fiasco

來源:互聯網
上載者:User
static initialization order fiasco是對C++的一個非常微妙的並且常見的誤解。不幸的是,錯誤發生在main()開始之前,很難檢測到。

簡而言之,假設你有存在於不同的源檔案x.cpp 和y.cpp的兩個靜態對象x 和 y。再假定y對象的建構函式會調用x對象的某些方法。

就是這些。就這麼簡單。

結局是你完蛋不完蛋的機會是50%-50%。如果碰巧x.cpp的編輯單元先被初始化,這很好。但如果y.cpp的編輯單元先被初始化,然後y的建構函式比x的建構函式先運行。也就是說,y的建構函式會調用x對象的方法,而x對象還沒有被構造。

如何防止“static initialization order fiasco”?

使用“首次使用時構造(construct on first use)”法,意思就是簡單地將靜態對象包裹於函數內部。

例如,假設你有兩個類,Fred 和 Barney。有一個稱為x的全域Fred對象,和一個稱為y的全域Barney對象。Barney的建構函式調用了x對象的goBowling()方法。 x.cpp檔案定義了x對象:

// File x.cpp
#include "Fred.hpp"
Fred x;

y.cpp檔案定義了y對象:

// File y.cpp
#include "Barney.hpp"
Barney y;

Barney建構函式的全部看起來可能是象這樣的:

// File Barney.cpp
#include "Barney.hpp"
Barney::Barney()
{
// ...
x.goBowling();
// ...
}

正如以上所描述的,由於它們位於不同的源檔案,那麼 y 在 x 之前構造而發生災難的機率是50%。

這個問題有許多解決方案,但一個非常簡便的方案就是用一個返回Fred對象引用的全域函數x(),來取代全域的Fred對象 x。

// File x.cpp
#include "Fred.hpp"
Fred& x()
{
static Fred* ans = new Fred();
return *ans;
}

由於靜態局部對象只在控制流程第一次越過它們的聲明時構造,因此以上的new Fred()語句只會執行一次:x()被第一次調用時。每個後續的調用將返回同一個Fred對象(ans指向的那個)。然後你所要做的就是將 x 改成 x():

// File Barney.cpp
#include "Barney.hpp"
Barney::Barney()
{
// ...
x().goBowling();
// ...
}

由於該全域的Fred對象在首次使用時被構造,因此被稱為首次使用時構造法(Construct On First Use Idiom)

這種方法的不利方面是Fred對象不會被析構。C++ FAQ Book有另一種技巧消除這個影響(但面臨了“static de-initialization order fiasco”的代價)。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.