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

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

谢谢

4

2 回答 2

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>;
于 2013-08-03T17:26:11.867 回答
2

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

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

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

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

于 2013-08-03T17:35:32.457 回答