C++ 使用模板并调用析构函数的单例

C++ 使用模板并调用析构函数的单例,c++,static,singleton,C++,Static,Singleton,我实现了一个单例类,如下所示 #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{

我实现了一个单例类,如下所示

#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();     
}
#包括
使用名称空间std;
模板
单件阶级{
受保护的:
静态T*s_实例;
公众:
T*实例(){
如果(s_实例){
返回s_实例;
}否则{
s_实例=新的T;
返回s_实例;
}
}
};
模板
T*singleton::s_实例;
A类:公共单身人士{
朋友班单身;
公众:
无效打印添加(){

有几件事值得注意:

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

确保在程序终止期间删除singleton实例的最简单方法(与操作系统获取的结果相反)就是使用具有函数范围的静态实例:

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();
模板
结构单体{
静态T&实例(){
静态T实例;
返回实例;
}
};
类Singleton客户端:公共Singleton{
朋友班单身;
SingletonClient()
{}
};
SingletonClient&s=Singleton::instance();
使用模板实现singleton有一些微妙之处。如果在多个翻译单元中使用singleton模板实例化,那么当您实际上只需要一个实例时,您可能最终会得到多个singleton客户端实例。解决此问题的方法是在头文件中使用extern模板声明客户端类和客户端实现文件中的模板实例化

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

// In the implementation file of SingletonClient:
template class Singleton<SingletonClient>;
//在SingletonClient的头文件中:
外部模板类单例;
//在SingletonClient的实现文件中:
模板类单例;

有几件事值得注意:

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

确保在程序终止期间删除singleton实例的最简单方法(与操作系统获取的结果相反)就是使用具有函数范围的静态实例:

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();
模板
结构单体{
静态T&实例(){
静态T实例;
返回实例;
}
};
类Singleton客户端:公共Singleton{
朋友班单身;
SingletonClient()
{}
};
SingletonClient&s=Singleton::instance();
使用模板实现singleton有一些微妙之处。如果在多个翻译单元中使用singleton模板实例化,那么当您实际上只需要一个实例时,您可能最终会得到多个singleton客户端实例。解决此问题的方法是在头文件中使用extern模板声明客户端类和客户端实现文件中的模板实例化

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

// In the implementation file of SingletonClient:
template class Singleton<SingletonClient>;
//在SingletonClient的头文件中:
外部模板类单例;
//在SingletonClient的实现文件中:
模板类单例;

对于每个T,都有自己的单例类专门化,它有自己的静态数据成员。因此对于A和B,它们是不同的

确实存在内存泄漏。有几种方法可以解决此问题。如果要保持惰性初始化,请对s_实例使用std::unique_ptr。然后将正确销毁对象。请注意,初始化不是线程安全的

您也可以只使用:
T s_实例
而不是
T*s_实例
。这样,对象在main()之前构造,也将被正确地销毁。这也意味着这是线程安全的


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

对于每个T,都有自己的单例类专门化,它有自己的静态数据成员。因此对于A和B,它们是不同的

确实存在内存泄漏。有几种方法可以解决此问题。如果要保持惰性初始化,请对s_实例使用std::unique_ptr。然后将正确销毁对象。请注意,初始化不是线程安全的

您也可以只使用:
T s_实例
而不是
T*s_实例
。这样,对象在main()之前构造,也将被正确地销毁。这也意味着这是线程安全的


另一种方法是将
静态T s_实例;
放在实例()内方法并返回它。这在C++11中保证是线程安全的。

请告诉我,在多个翻译单元中,如果没有extern关键字,它将如何成为多个实例?是的。规则是,每个翻译单元将实例化模板,使静态对象的实例出现在每个生成的对象文件中。只要所有目标文件都链接到同一个二进制文件中,就不会有问题,因为链接器会将所有实例折叠到一个实例中。但是,如果将目标文件编译到不同的二进制文件中,则每个二进制文件中都会有不同的实例。这可能是可以接受的,也可能是不可以接受的,具体取决于你能告诉我,在多个翻译单元中,如果没有extern关键字,它将如何成为多个实例吗?是的。规则是,每个翻译单元将实例化模板,使静态对象的实例出现在每个生成的对象文件中。只要所有的对象文件都链接到相同的二进制文件则不会有问题,因为链接器会将所有实例折叠为单个实例。但是,如果将对象文件编译为不同的二进制文件,则最终会得到不同的实例