2009-09-07 63 views
3

反正我可以修改這個代碼示例在C++中模擬靜態構造函數?

#include <stdlib.h> 
#include <iostream> 

class Base { 
public: 
    Base() { 
     if(!m_initialized) { 
      static_constructor(); 
      m_initialized = true; 
     } 
    } 
protected: 
    virtual void static_constructor() { 
     std::cout << "Base::static_constructor()\n"; 
    } 
private: 
    static bool m_initialized; 
}; 

bool Base::m_initialized = false; 

class Derived : public Base { 
    void static_constructor() { 
     std::cout << "Derived::static_constructor()\n"; 
    } 
}; 

int main(int argc, char** argv) { 
    Derived d; 
    return(EXIT_SUCCESS); 
} 

這樣Derived::static_constructor()被調用,而不是基地的?我想初始化一堆靜態變量,最合理的地方是在類中的某處。

+0

剛剛意識到我不能真的使用'm_initialized',因爲那麼只有一個派生類可以永遠得到初始化... – mpen 2009-09-07 21:20:20

+0

我基本上只需要從基地代碼複製到每個派生類? – mpen 2009-09-07 21:21:11

+0

不直接;但有辦法獲得你想要的東西。你需要初始化器在啓動時運行還是第一次實例化? – bdonlan 2009-09-07 21:27:10

回答

4

我從Martin V Lowis的解決方案中採用了這個解決方案。主要的區別是,它使用了多重繼承,而CRTP:

template<class T> 
class StaticInitializer : public T 
{ 
    static bool initialized; 
public: 
    StaticInitializer(){ 
    if(!initialized){ 
     T::static_constructor(); 
     initialized=true; 
    } 
    } 
}; 

template<class T> bool StaticInitializer<T>::initialized; 

class Base : public StaticInitializer<Base> 
{ 
public: 
    static void static_constructor() { 
    std::cout << "Base::static_constructor()\n"; 
    } 
}; 
static Base _base; 

class Derived : public Base, public StaticInitializer<Derived> 
{ 
public: 
    static void static_constructor() { 
     std::cout << "Derived::static_constructor()\n"; 
    } 
}; 
static Derived _derived; 

StaticInitializer的每一個具體子類得到它自己的靜態構造函數初始化方法,但是你一直有真正繼承的優勢。

+0

啊!現在好多了。不幸的是,您必須從派生類中的StaticInitializer繼承,但我認爲這是我們能做的最好的。很酷!謝謝 – mpen 2009-09-07 23:22:47

+0

你怎麼實際編譯這段代碼/ – lalitm 2009-12-20 08:27:46

3

您可以避免重複使用模板類的布爾變量。聲明其構造函數將運行靜態初始化程序的模板的實例。使實例成爲靜態的,以便包含頭文件將自動聲明一個靜態對象。

#include <iostream> 
using namespace std; 

template<class T> 
class StaticInitializer{ 
    static bool initialized; 
public: 
    StaticInitializer(){ 
    if(!initialized){ 
     T::static_constructor(); 
     initialized=true; 
    } 
    } 
}; 

template<class T> bool StaticInitializer<T>::initialized; 

class Base{ 
public: 
    static void static_constructor() { 
    std::cout << "Base::static_constructor()\n"; 
    } 
}; 
static StaticInitializer<Base> _base; 

class Derived{ 
public: 
    static void static_constructor() { 
     std::cout << "Derived::static_constructor()\n"; 
    } 
}; 
static StaticInitializer<Derived> _derived; 

int main() 
{} 
+0

就像這個解決方案一樣好...然後基礎和派生的類型是'StaticInitializer',我不能使用他們的其他成員函數... – mpen 2009-09-07 21:45:23

5

您不應該從構造函數(或析構函數)調用虛函數!結果不會像「預期」那樣(因此您看到的結果)。爲什麼?因爲基礎構造函數(Base)在Derived構造函數之前被調用。這意味着虛擬函數可能引用的Derived中的本地數據成員尚未初始化。另外,甚至更重要的是,vtable尚未使用Derived中的函數進行初始化,但僅限於來自Base的成員。因此,虛擬函數並不是真正的虛擬 - 它不會直到Base()完成並且Derived()被處理。

另外,這樣做會破壞Open/Closed-principle,簡而言之,它們會顯示「類應該對擴展打開,但對修改關閉」。您正在修改基本靜態初始化,嘗試修改其行爲而不是擴展它。這似乎是在當時是個好主意,但沒準就會稍後咬你的屁股;)

+0

啊..這是有道理的。它不回答我的問題,但這是一個非常有用的解釋。 +1 – mpen 2009-09-07 22:07:13

+0

+1表示vtable的說明。 – RichieHindle 2009-09-08 21:38:53

0

我提出這個解決方案:

#include <cstdlib> 
#include <iostream> 

class Object { 
public: 
    Object() { 
     std::cout << m_var << std::endl; 
    } 
private: 
    static int m_var, init_var(); 
}; 

int Object::init_var() { 
    return 5; 
} 

int Object::m_var = Object::init_var(); 

int main(int argc, char** argv) { 
    Object o; 
    return(EXIT_SUCCESS); 
} 

這樣m_var只被初始化一次,我不斷班級內的所有施工代碼。