2011-03-15 142 views
4

這涉及到的對象在C初始化++模仿C++靜態構造函數

我有一組類(沒有實例),從一個共同的基類繼承,我需要他們登記有關信息問題他們自己在一個容器(特別是地圖)中,當程序啓動時。

問題是我需要它是動態的。容器在獨立項目中定義,與類不同。我寧願避免製作多個硬編碼版本的庫,每個程序使用它的每一組類都有一個版本。

我想過在這些子類的每一箇中都有一個特殊類的靜態實例,這會在其構造函數中進行註冊。但是,我發現沒有辦法保證在建造這些物品之前集裝箱將被建造。

我還應該注意,在創建這些子類的任何實例之前,容器中關於子類的信息應該可用。

有沒有辦法做到這一點,或模仿C++中的靜態構造函數?

+0

你不能讓註冊表對象(地圖),其通過的getInstance動態創建一個單身()? – Kevin 2011-03-15 18:51:16

回答

1

您正在一次描述不同的問題。在某種類型的靜態初始化的特定問題上,一種簡單的方法是創建一個將執行註冊的僞類。然後,每一個不同的類都可以有一個static const X成員,成員必須在翻譯單元中定義,並且定義將觸發實例的實例化和類的註冊。

這並沒有解決難以解決的問題,這是創始化命令的失敗。該語言不對任何翻譯單元中對象的初始化順序提供任何保證。也就是說,如果你用這樣的類編譯三個翻譯單元,則不能保證假成員的相對執行順序。這也適用於庫:如果這樣的容器是全局/靜態成員屬性,則不能保證你想要註冊類的容器已經被初始化。

如果您有權訪問該代碼,則可以修改容器代碼以使用static local variables,這將向前邁出一步,以確保初始化順序。作爲一個可能的解決方案的草圖:

// registry lib 
class registry { // basically a singleton 
public: 
    static registry& instance() { // ensures initialization in the first call 
     static registry inst; 
     return inst; 
    } 
// rest of the code 
private: 
    registry(); // disable other code from constructing elements of this type 
}; 
// register.h 
struct register { 
    template <typename T> 
    register(std::string name) { 
     registry::instance().register(name, T (*factory)()); // or whatever you need to register 
    } 
}; 
// a.h 
class a { 
public: 
    static a* factory(); 
private: 
    static const register r; 
}; 
// a.cpp 
const register a::r("class a", a::factory); 
// b.h/b.cpp similar to a.h/a.cpp 

現在,在這種情況下,是ab類的登記中沒有明確的命令,但可能不會是一個問題。在另一方面,通過在registry::instance功能使用局部靜態變量單例的初始化保證要被執行之前registry::register方法中的任何呼叫(作爲第一呼叫到instance方法的一部分)。

如果你不能做出改變,你基本上運氣不好,你不能保證registry將在其他翻譯單元中的其他靜態成員屬性(或全局變量)之前實例化。如果是這樣的話,那麼你將不得不推遲該類的註冊到第一個實例化,並且將代碼添加到要註冊的每個類的構造函數中,以確保該類在之前在對象的實際構造之前註冊了

這可能或不是解決方案,具體取決於其他代碼是否創建類型的對象。在工廠函數的特定情況下(首先想到的是),如果沒有其他東西被允許創建類型爲ab的對象...則構造函數調用中的小豬支持註冊也不是解決方案。

+0

謝謝。這看起來像我在找什麼。至於最後一件事,你是否指的是編譯器可能會忽略編譯類的可能性,如果它不是在某處構建的? – Chris 2011-03-15 19:25:29

+0

不是真的,而是因爲如果沒有關於代碼的功能的信息,很難判斷是否實際上在其他地方實例化了這些類型的對象。如果你的代碼沒有創建'a'的對象,那麼向'a'構造函數添加註冊調用將沒有任何作用 - 沒有調用的構造函數=>不執行註冊 – 2011-03-15 20:13:56

0

這是Singleton pattern的候選人。基本上,您希望容器在實例化子類的第一個實例時被實例化。這可以通過檢查基類構造函數中的單例指針是否爲NULL來實現,如果是,則實例化容器。

0

其中一個想法是將註冊仿函數傳遞給類。每個後代都會執行該功能進行註冊。這個仿函數可以在構造函數中傳遞。

實施例:

struct Registration_Interface 
{ 
    virtual void operator() (const std::string& component_name) = 0; 
}; 

struct Base 
{ 
}; 

struct Child1 
    : public Base 
{ 
    Child(Registration_Interface& registration_ftor) 
    { 
    //... 
    registration_ftor("Child1"); 
    } 
}; 
0

參見:http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14

一種選擇是懶惰地,當第一件事被添加到它構造容器:

void AddToContainer(...) { 
    // Will be initialized the first time this function is called. 
    static Container* c = new Container(); 
    c->Add(...); 
    } 

的唯一方法「模仿「一個靜態構造函數是顯式調用一個函數來執行你的靜態初始化。僅通過鏈接模塊,沒有其他方式可以在代碼之前運行代碼。

1

它反對OOP範式,但如何讓靜態成員形成由2個全局變量引導的鏈表?你可以做這樣的事情:

ClassRegistrator *head=NULL; 
ClassRegistrator *tail=NULL; 

struct ClassRegistrator { 
    ... //data that you need 
    ClassRegistrator *next; 
    ClassRegistrator(classData ...) { 
     if (head==NULL) head=tail=this; 
     else { 
     tail->next=this; 
     tail=this; 
     } 
     ... //do other stuff that you need for registration 
    } 
}; 


class MyClass { //the class you want to register 
    static ClassRegistrator registrator; 
} 

ClassRegistrator MyClass::registrator(...); //call the constructor 

相信全局變量,因爲他們不需要有構造,但都只是純粹的數據,保證當你開始進行初始化已執行你的代碼。

顯然這不是線程安全的等,但應該使你的工作完成。

0

您可能會使用「initialise on first use」模式,然後實例化一個虛擬靜態實例,以確保儘早進行初始化。

class cExample 
{ 
    public : 
     cExample() ; 

     // Static functions here 

    private : 
     static bool static_init ; 

     // other static members here 
} 

cExample::static init = false ; 

cExample::cExample() 
{ 
    // Static initialisation on first use 
    if(!static_init) 
    { 
     // initialise static members 
    } 

    // Instance initialisation here (if needed) 
} 

// Dummy instance to force initialisation before main() (if necessary) 
static cExample force_init ;