Posted on 2009-08-27 18:20
魔のkyo 阅读(542)
评论(2) 编辑 收藏 引用
今天在检查内存泄漏时,查到了一些我写的单件,结构大致如下
class A
{
public:
static A* Instance()
{
if(s_pInstance == NULL)
{
s_pInstance = new A;
}
return s_pInstance;
}
private:
A(){}
private:
static A* s_pInstance;
};
因为我需要的这个单件的生存周期和作用域都是全局,所以没有释放的必要,在程序结束时操作系统自然会收回内存,但是在检测内存泄漏的时候就添乱了。这种写法还有一个潜在问题是,如果A中有一个成员对象,它的析构函数必须在程序结束时被执行,比如它需要做将缓冲区写入文件之类的事,而单件没有被析构会导致这个成员对象没有执行到析构函数,这就引起了问题。
方法1:一种解决方法是为单件添加static void Release();然后在程序结束的时候显式的调用,这显然不是很优雅。
方法2:还有一种解决方法是,不使用静态指针指向实例,而是用一个静态成员,程序自动会在开始时执行构造,结束时执行析构。这种方法的问题是,程序员不能手动控制单件的初始化时间,如果单件A对单件B有依赖,而不能保证单件B先于单件A被构造,这就产生了新的问题。
方法3:今天和同项目的同事(ZCY)讨论到这个问题,他提到了一种方法可以同时解决上面两个问题,将A封装在另一个单件B里,B使用方法2的结构,在B中存放一个指向A的指针,在第一次调用B::GetInstance的时候创建A的实例,而在B的析构函数中释放A的实例,这样既可以控制单件的创建时间,又可以保证程序结束时自动释放。但是还存在一些问题,总感觉我需要用A的时候要通过B的GetInstance取得有点别扭,如果对于每个单件都写一层这样的封装显然会增加代码量,我们又如何避免A被其他地方当成非单件进行构造呢?
方法4:根据ZCY的思路我写了下面的一个单件模板,直接上代码
#include <iostream>
using namespace std;
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
template <class T>
class Singleton
{
public:
static T* Instance()
{
return s_Instance.GetSubject();
}
static void Release()
{
s_Instance.ReleaseSubject();
}
~Singleton()
{
ReleaseSubject();
}
private:
T* GetSubject()
{
if(m_pSubject == NULL)
{
m_pSubject = new T;
}
return m_pSubject;
}
void ReleaseSubject()
{
if(m_pSubject)
{
delete m_pSubject;
m_pSubject = NULL;
}
}
private:
T* m_pSubject;
static Singleton s_Instance;
};
template <typename T>
Singleton<T> Singleton<T>::s_Instance;
#define SINGLETON(T) friend class Singleton<T>;
class A
{
SINGLETON(A)
public:
int a;
private:
A()
{
cout<<"Create A"<<endl;
}
~A()
{
cout<<"Delete A"<<endl;
}
};
void fun()
{
Singleton<A>::Instance()->a = 123456;
}
int main()
{
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
cout<<"Hello world"<<endl;
fun();
cout<<Singleton<A>::Instance()->a<<endl;
Singleton<A>::Release();
Singleton<A>::Instance()->a = 1;
cout<<Singleton<A>::Instance()->a<<endl;
}
控制台输出
Hello world
Create A //用户可以控制单件何时被创建
123456
Delete A //如果有需要,用户可以手动释放单件
Create A
1
Delete A //单件会在程序结束时自动释放
编译器显示没有内存泄漏
Perfect!
但我还是期望有一种能解决上面提到的两个问题,而且使用简便的,并且可以通过A::Instance()取得实例的方法而不是通过Singleton<A>::Instance(),如果有想法请回复我或者给我留言