2014-04-01 13 views
3
class Singleton 
{ 
private: 
    static Singleton s; 
    Singleton(){} 
public: 
    static Singleton *getInstance() 
    { 
     return &s; 
    } 
}; 

Singleton Singleton::s; 

這是一個有效的單身類?有效的單身類?

class Singleton 
{ 
private: 
    static Singleton *m_instance; 
    Singleton(){} 
public: 
    static Singleton *getInstance() 
    { 
     return m_instance; 
    } 

}; 
Singleton * Singleton::m_instance = new Singleton; 

class Singleton 
{ 
private: 
    static Singleton *m_instance; 
    Singleton(){} 
public: 
    static Singleton *getInstance() 
    { 
     if(m_instance == NULL) 
     { 
      lock(); 
      if(m_instance == NULL) 
       m_instance = new Singleton; 
      unlock(); 
     } 
     return m_instance; 
    } 

}; 
Singleton * Singleton::m_instance = NULL; 

三個單班以上都是線程安全的,但他們都傾向於「靜態變量初始化順序的悲劇」,對嗎?

回答

5

這是一個有效的單身類?

現在,編輯後的答案是肯定的,它是有效的&它也是線程安全的,因爲所有的非功能範圍的靜態變量以前建造main(),而只有一個活動線程。

C++標準n3337§3.6.2/1§3.6.2/2:那些具有 靜態的:非局部變量

有兩大類名爲非局部變量的初始化存儲時間(3.7.1)以及線程存儲時間爲 (3.7.2)。具有靜態存儲持續時間的非局部變量 由於程序啓動而被初始化。具有線程存儲持續時間的非局部變量 被初始化爲線程執行的結果。在每個啓動階段中,初始化如下進行。

在任何其他初始化發生之前,持續時間(3.7.2)的靜態存儲持續時間(3.7.1)或線程存儲 的變量應該被初始化爲零(8.5)。執行恆定初始化:

- 如果每個全表達(包括隱式轉換),該 出現在與靜態或螺紋 存儲持續時間的基準的初始值設定爲常量表達式(5.19)和參考被 勢必一個左值指定一個靜態存儲時間爲 的對象或臨時對象(見12.2);

- 如果與靜態或線程存儲持續時間的目的是通過一個構造函數調用初始化 ,如果構造爲constexpr構造, 如果所有構造器參數是常量表達式(包括 轉換),並且如果經過函數調用替換(7.1.5), mem-initializers中的每個構造函數調用和全表達式,以及非靜態數據成員的括號或等於初始值設定項中的全表達式是 的常量表達式;

- 如果具有靜態或線程存儲持續時間的對象不是由構造函數調用初始化的 ,並且其初始值設定項中出現的每個完整表達式都是常量表達式。

一起,零初始化和常量初始化被稱爲 靜態初始化;所有其他的初始化都是動態的 初始化。在動態初始化發生之前,應執行靜態初始化。 (...)

C++標準n3337§6.7/4:聲明陳述

所有塊範圍變量的零初始化(8.5)與靜態 存儲持續時間(3.7.1)或螺紋在進行任何其他初始化之前執行的存儲持續時間(3.7.2)爲 。常量 具有靜態存儲的塊範圍實體的初始化(3.6.2) 持續時間(如果適用)在其塊首先輸入 之前執行。允許執行早期 初始化其他塊範圍變量與靜態或線程 存儲持續時間在相同的條件下,實現爲 允許靜態初始化靜態或變量線程 存儲持續時間在命名空間範圍內。否則,這種變量是 第一次控制通過它的聲明初始化; 這樣的變量在其初始化完成後被認爲是初始化的。如果通過拋出異常退出初始化, 的初始化沒有完成,所以它會再次嘗試下一個 時間控制進入聲明。 如果控制在初始化變量時同時輸入聲明 ,則執行的併發執行應等待初始化*)的完成。(...)

*):

實行不得引進各地的 初始化執行任何僵局。

但它仍然傾向於static initialization order fiasco。寫getInstance的常用方法是:

Singleton& getInstance() 
{ 
    static Singleton instance; 
    return instance; 
} 

這樣就可以避免這個初始化的問題。

這是線程安全的單身類?

在上面的代碼C++ 11是線程安全的。在C++ 03可以使用

pthread_once


除此之外,你也應該從拷貝和賦值預防:

Singleton(Singleton const&);  // Don't Implement 
void operator=(Singleton const&); // Don't implement 
+0

我忘記了初始化代碼。 – tenos

+0

爲什麼在C++ 11中只有線程安全? – tenos

+0

之前C++ 11對象初始化可能已經由多線程完成 – 4pie0

3

據我所知,這是線程安全的。但它易受static initialization order fiasco的影響。

如果對象嘗試訪問它的構造Singleton和對象程序初始化期間構造這個代碼是在另一個編譯單元比Singleton,它可能會或可能不會崩潰,因爲Singleton::s可能會或可能還未被初始化(因爲編譯單元中靜態對象的初始化順序未定義)。這裏是一個例子:

// in another compilation unit, far far away 
struct Foo { 
    Foo() { 
     Singleton::getInstance(); 
    } 
}; 
Foo foo; 
+0

你能舉個例子嗎? – 4pie0

+0

@lizusek,我已經添加了一個例子。 – user2079303

+1

並且初始化順序的問題是在C++中使用單例的主要原因之一。 –

0

這是懶惰的初始化單例,是的。它在C++ 11下是線程安全的。

+0

_'這是懶惰的初始化Singleton,是的......'_不,它不是! –