簡體   English   中英

具有靜態成員的類在C ++中包含靜態成員

[英]Classes with static members containing static members in C++

我有一個包含靜態成員'obj'的類。 靜態類成員obj本身包含一個靜態成員(碰巧是類的互斥體類型)。

現在,當我的程序終止時,它崩潰了。 當靜態對象'obj'被破壞時,會發生這種情況。 'obj'的析構函數調用其靜態成員(我自己的RAII慣用的互斥鎖類型來破壞底層的低級對象)。 不幸的是,由於未定義靜態對象的初始化順序(->反析構順序),因此該成員已經被破壞。

如何從中干凈地生存? 我覺得這沒有多發生。 通常,擁有靜態的非POD成員看起來非常危險。 特別是如果您不太了解其內部結構。

如果使它們在函數內部是static ,則將根據誰先調用函數來對它們的生存期進行排序。 這就是單例易於實現的方式。

foo& get_foo() {
  static foo instance;
  return instance;
}

bar& get_bar() {
  static bar instance;
  return instance;
}

不過,最好還是避免使用靜態變量。

嘗試避免動態分配時,這是常見的陷阱。

我避免此問題的方法是遵循“靜態類”的模式-對於沒有靜態數據的實例,通常靜態數據屬於“ manager”類。 然后,通過顯式的“ initialise”調用處理靜態數據初始化,並在僅具有靜態數據和靜態成員函數的manager類上,通過顯式的“ shutdown”調用處理破壞操作。

您可能必須使用new或delete並明確說明正在執行的操作-在這種情況下,您會否定自動機制為您工作的優勢,但是作為交換,您將獲得可靠且易於調試的初始化和關閉例程。

這種方法實質上創建了一個單例,因此沒有管理器類的實例-基本上是靜態數據的加載,並且某些C函數具有類語法,從而提供了封裝的好處(例如,在類外部無法訪問私有成員)

在許多情況下,這對編譯器也很友好,因為它知道所有數據在哪里以及如何處理

如果需要在構造函數或析構函數中由靜態對象使用的對象,通常最好在單例模式上使用變體。 這解決了初始化問題的順序,如果正確執行,對象將永遠不會被破壞,因此也不應該存在破壞順序的問題。

由於您的應用程序顯然是多線程的(因為您有互斥鎖),因此應采取通常的預防措施,通過確保在輸入main之前進行初始化來使對象線程安全。 基本想法是這樣的:

template <typename T, char const* id>
class StaticInstanceWrapper
{
    static T* myObject;
public:
    static T& instance();
};

template <typename T, char const* id>
T* StaticInstanceWrapper<T, char const* id>::myObject =
        &StaticInstanceWrapper<T>::instance();

template <typename T, char const* id>
T& StaticInstanceWrapper<T>::instance()
{
    if ( myObject == NULL ) {
        myObject = new T;
    }
    return *myObject;
}

然后,將靜態對象定義為static StaticInstanceWrapper<Whatever> obj; ,然后將其作為obj.instance().someFunction()而不是obj.someFunction() (因為obj沒有someFunction()成員,因此無法編譯)。

請注意,對於每個StaticInstanceWrapper實例StaticInstanceWrapper ,其類型都是不同的,並且具有唯一的靜態成員,則必須強制對模板進行單獨的實例化。 這就是為什么我們使用id模板參數的原因; 只要每個實例都具有唯一的標識符,則該參數的類型實際上可以是任意值。 在實踐中,我可能會使用宏進行定義,類似於以下內容:

#define DEFINE_STATIC_INSTANCE_WRAPPER(type, name) \
char const PASTE(name, _Identifier)[] = STRINGIZE(name); \
StaticInstanceWrapper<type, PASTE(name, _Identifier)> name

這樣可以確保每個實例都有唯一的ID。 (如果您想變得更高級,也可以在__LINENO__中進行__LINENO__ ,但是由於該名稱在范圍上必須是唯一的,因此我懷疑這是必要的。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM