posts - 225, comments - 62, trackbacks - 0, articles - 0
   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
今天在检查内存泄漏时,查到了一些我写的单件,结构大致如下
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()->= 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()->= 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(),如果有想法请回复我或者给我留言

Feedback

# re: 关于单件引起的(伪)内存泄漏  回复  更多评论   

2009-10-20 13:29 by fietiger
我比较认同你的做法,我觉得基于你的代码可以将一般类单件化

# re: 关于单件引起的(伪)内存泄漏  回复  更多评论   

2010-03-04 21:54 by 魔のkyo
@fietiger
恩,我现在也觉得这样不错
只有注册用户登录后才能发表评论。