【问题标题】:singleton using template and calling destructor使用模板和调用析构函数的单例
【发布时间】:2013-08-03 16:00:55
【问题描述】:

我有一个如下实现的单例类。

#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();     
}

如何为此调用析构或。 看起来上面没有“删除”行,valgrind 显示 0 个内存泄漏。不删除指针,我是否泄漏了内存?还是实现单例的方法不对? 对于这两个类,我们有一个共同的静态成员。基本上这里的不同对象的静态成员有什么不同?

谢谢

【问题讨论】:

    标签: c++ static singleton


    【解决方案1】:

    对于每个 T,都有它自己的单例类特化,它有自己的静态数据成员。所以对于 A 和 B,这些是不同的。

    你确实泄漏了内存。有几种方法可以解决它。如果您想保持惰性初始化,请使用 std::unique_ptr 作为 s_instance。然后对象将被正确销毁。请注意,您的初始化不是线程安全的。

    您也可以只使用:T s_instance 而不是 T* s_instance。这种方式对象是在 main() 之前构造的,并且也会被正确地破坏。这也意味着这是线程安全的。

    另一种方法是在 instance() 方法中使用 static T s_instance; 并返回它。这保证在 C++11 中是线程安全的。

    【讨论】:

      【解决方案2】:

      有几点值得注意:

      实际上,您并没有泄漏内存。只能创建该类的单个实例(这意味着泄漏不会导致过多的资源使用),并且当客户端进程终止时,操作系统将重新获得分配给该实例的内存。

      确保在程序终止期间删除单例实例而不是由操作系统获取的最简单方法是使用具有函数范围的静态实例:

      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>;
      

      【讨论】:

      • 能否请您告诉我,在多个翻译单元中,如果没有 extern 关键字,怎么会有多个实例?
      • 是的。规则是每个翻译单元都会实例化模板,导致静态对象的实例出现在每个生成的对象文件中。只要所有目标文件都链接到同一个二进制文件中,就不会有问题,因为链接器会将所有实例折叠成一个实例。但是,如果您将目标文件编译成不同的二进制文件,您最终会在每个二进制文件中得到不同的实例。这可能会也可能不会接受,具体取决于您的代码的意图。
      猜你喜欢
      • 1970-01-01
      • 2020-07-21
      • 2018-04-02
      • 2018-12-19
      • 2015-10-05
      • 2012-07-24
      • 2014-05-18
      • 2015-06-04
      • 2023-03-23
      相关资源
      最近更新 更多