2012-07-08 146 views
4

我有一個抽象的單例類。我的目標是任何子類都必須實現init()函數AND NOTHING ELSE。以下是我所做的:C++單例模板類繼承

template <typename T> 
class Singleton 
{ 
    public: 

     Singleton() 
     { 
      init(); 
     } 

     static T& instance() 
     { 
      static T instance; 
      return instance; 
     } 

    protected: 
     virtual void init() = 0; 
}; 

class SubSingleton : public Singleton<SubSingleton> 
{ 
    protected: 
     void init() 
     { 
      cout << "Init SubSingleton" << endl; 
     } 
}; 

由於init()是受保護的,不能從公共靜態函數中調用,因此不會編譯。這個問題有兩種解決方案。首先我們可以公開init()函數,但我不想公開這個函數。因此,這僅留下第二個解決方案,如下更改子類:

class SubSingleton : public Singleton<SubSingleton> 
{ 
    friend class Singleton<SubSingleton>; 

    protected: 

     void init() 
     { 
      cout << "Init SubSingleton" << endl; 
     } 
}; 

這工作完全,但我不希望朋友聲明,因爲其他程序員可能會延長我的代碼,並且可能不知道,這應該是添加。

有沒有其他的方式來實現這個沒有朋友的聲明?也許有什麼來自Andrei Alexandrescu?

編輯:現在在構造函數中調用init函數而不是實例()函數。編輯2:出於技術原因和兼容性我需要一個init()函數,不能只是在構造函數中進行初始化。

鑄造工作的解決方案,但如果我從構造函數調用init()鑄造不再工作。有什麼建議麼?

+3

調用'實例()'兩次會導致'初始化()'被調用兩次:這是正常的嗎? – kennytm 2012-07-08 08:44:35

+0

什麼是你的模板 Singleton;'?它似乎並不需要派生類實際上是單例,所以讓派生類在構造函數中執行它們的初始化並不像通常那樣簡單嗎? (事實上​​,即使你想要派生類是單例,我仍然沒有看到在構造函數後總是立即調用的「init」函數的原因。) – 2012-07-08 08:49:12

+0

第一:你現在不創建實例,第二:一個bool isInit會有所幫助,但是你期望這裏可能是一個Create而不是一個Init。似乎現在有點混淆了。 – 2012-07-08 08:50:00

回答

3

問題是,你不使用虛擬函數,你打算使用。我現在在行動中沒有多態現象。爲什麼?因爲你在直接派生對象上調用init()。使用init()函數是虛擬的,當你在基類引用或基類指針上調用init()時,如下所示。然後調用發生在基類的範圍內,因爲instance()方法是一個基類方法,所以從中調用一個受保護的方法是非常好的。

static T& instance() 
    { 
     static T myInstance; 
     Singleton<T>& t = myInstance; // Just define a dummy reference here. 
     t.init(); 
     return myInstance; 
    } 
2

看來你混的2種polimorphysms的:使用CRTP如果你想SubSingleton::init()「靜態」叫

1),也就是說,你不需要在基類來定義它 - 但是那麼你真的必須讓它可以訪問基類。

2)但是,如果你定義的init()作爲虛擬的,你使用動態polimorphism,並且不需要進行init()市民:

static T& instance() 
{ 
    static T instance; 
    static_cast<Singleton<T> &>(instance).init(); 
    return instance; 
} 
+0

+1好點,但是你使用'instance'作爲函數模板和它的主體中的一個變量。這不會編譯。 – Walter 2012-07-08 09:10:26

+0

@Walter能否引用標準的paragrhaph來禁止這個?據我記得這是允許的。 – 2012-07-08 09:35:18

+0

也許這是允許的,但它真的不會編譯,由於ambigious的名字 – 2012-07-08 10:04:51

3

你根本不需要這個init()的東西。這隻會造成問題。

template <typename T> 
class Singleton 
{ 
    public: 
     static T& instance() 
     { 
      static T instance; 
      return instance; 
     } 
}; 

class SubSingleton : public Singleton<SubSingleton> 
{ 
    public: 
     SubSingleton() 
     { 
      cout << "Init SubSingleton" << endl; 
     } 
}; 

這樣,你不僅能去除不必要的東西 - 但你也從調用的init()每次有人打電話instanse()時防止...