2013-08-03 26 views
1

我有一個單例類實現如下。使用模板和調用析構函數的單例

#include <iostream> 
using namespace std; 

template<class T> 
class singleton{ 
protected: 
    static T* s_instance; 
public: 
    T* instance(){ 
     if(s_instance){ 
      return s_instance ; 
     }else{ 
      s_instance = new T; 
      return s_instance; 
     } 
    } 
}; 
template <class T> 
T* singleton<T>::s_instance; 

class A:public singleton<A>{ 
    friend class singleton; 
public: 
    void print_add(){ 
    cout<<"I AM A"<<endl; 
    cout<<s_instance<<endl; 
    } 
    ~A(){ 
     //delete s_instance; 
     cout<<"Dest A"<<endl; 
    } 
private: 
    A(){} 
}; 

class B:public singleton<B>{ 
    friend class singleton; 
public: 
    void print_add(){ 
    cout<<"I AM B"<<endl; 
    cout<<s_instance<<endl; 
} 
~B(){ 
     cout<<"Dest B"<<endl; 
     //delete s_instance; 
    } 
private: 
    B(){} 
}; 

int main(){ 
    A* a, *c; 
    B* b; 
    a->instance()->print_add(); 
    b->instance()->print_add(); 
    c->instance()->print_add();  
} 

如何調用destruct-或爲此。 它看起來像沒有上面的「刪除」行,valgrind顯示0內存泄漏。沒有刪除指針,我泄漏內存?或者實現單例的方法是錯誤的? 對於這兩個類我們有一個共同的靜態成員。基本上這裏的靜態成員對於不同的對象有何不同?

感謝

回答

2

對於每個T,都有它自己的singleton類特化,它有它自己的靜態數據成員。所以對於A和B來說這些是不同的。

你會泄漏內存。有幾種方法可以解決這個問題。如果你想保持你的懶惰初始化,請爲s_instance使用std :: unique_ptr。然後對象將被正確銷燬。請注意,您的初始化不是線程安全的。

您也可以只是:T s_instance而不是T* s_instance。這種方式對象是在main()之前構造的,並且也會被正確地破壞。這也意味着這是線程安全的。

另一種方法是在instance()方法內部有static T s_instance;並返回它。這保證在C++ 11中是線程安全的。

2

有值得注意的幾件事情:

實事求是地講,你是不是泄漏內存。只能創建一個類的實例(這意味着泄漏不會導致過多的資源使用),並且分配給該實例的內存將在客戶端進程終止時由操作系統收回。

以確保單實例程序終止時被刪除,而不是由OS收穫最簡單的方法,就是使用靜態實例與功能範圍:

template<class T> 
struct Singleton { 
    static T& instance() { 
    static T instance_; 
    return instance_; 
    } 
}; 

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

    SingletonClient() 
    {} 
}; 

SingletonClient &s = Singleton<SingletonClient>::instance(); 

有一些細微之處,以用模板實現單身人士。如果您在多個翻譯單元中使用單例模板實例化,那麼當您真的只需要一個單例客戶端時,實際上最終可能會包含單例客戶端的多個實例。處理這個問題的方法是在客戶端類的頭文件中使用extern模板聲明,並在客戶端的實現文件中使用模板實例。

// In header file of SingletonClient: 
extern template class Singleton<SingletonClient>; 

// In the implementation file of SingletonClient: 
template class Singleton<SingletonClient>; 
+0

可否請您告訴我,在多個翻譯單元中,它將如何超過一個實例,而不使用extern關鍵字? – neeru

+1

是的。規則是每個翻譯單元都會創建模板,導致靜態對象的一個​​實例出現在每個生成的目標文件中。只要所有的目標文件都鏈接到相同的二進制文件中,那麼就不會有問題,因爲鏈接程序會將所有實例合併爲一個實例。但是,如果將目標文件編譯爲不同的二進制文件,則最終將在每個二進制文件中創建不同的實例。這可能會或可能不會被接受,具體取決於代碼的意圖。 –