单身人士使用模板和调用析构函数

时间:2013-08-03 16:00:56

标签: 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{
           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内存泄漏。没有删除指针,我泄漏了内存吗?或者实施单身人士的方法是错误的? 对于这两个类,我们有一个共同的静态成员。基本上静态成员在这里对不同的对象有何不同?

由于

2 个答案:

答案 0 :(得分: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>;

答案 1 :(得分:2)

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

你的内存泄漏了。并且有几种方法可以解决它。如果要保持延迟初始化,请将sd :: unique_ptr用于s_instance。然后对象将被正确销毁。请注意,您的初始化不是线程安全的。

您也可以只使用T s_instance代替T* s_instance。这样,对象在main()之前构造,并且也将被正确地破坏。这也意味着这是线程安全的。

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