现在代码中越来越多的使用单体类,而我们通常的编写代码的方式是:

在A.h文件中:

class CTest
{
    public:
        static CTest* _ins;
        static CTest* Ins()
};

在A.cpp中:

CTest* CTest::_ins = NULL;
CTest* CTest::Ins()
{
    if ( !_ins )
    {   
        try 
        {   
            _ins = new CTest();
        }   
        catch(…)
        {   
            return NULL;
        }   
    }   
    return _ins;
}

而实际上,上面的代码通过valgrind检查内存泄漏的时候,会告诉你内存still reachable,虽然实际上当进程退出的时候,这些内存是实际释放掉了,但是还是多少会对内存泄漏的定位产生影响,而且也不符合谁申请谁释放的原则。

我们可以简单的使用stl的智能指针解决这个问题,即代码更改如下:

在A.h文件中:

class CTest
{
    public:
        static auto_ptr<CTest> m_auto_ptr;
        static CTest* _ins;
        static CTest* Ins();
};

在A.cpp中:

auto_ptr<CTest> CTest::m_auto_ptr;
CTest* CTest::_ins = NULL;
CTest* CTest::Ins()
{
    if ( !_ins )
    {   
        try 
        {   
            _ins = new CTest();
        }   
        catch(…)
        {   
            return NULL;
        }   
    }   
    return _ins;
}
CTest::CTest()
{
    m_

auto_ptr = auto_ptr<CTest>(this);
}

OK,这样valgrind的内存检查就可以通过了,但是实际上构建一个单体类要比我们的代码复杂的多,比如要考虑copy构造函数的问题,因为有可能有人会这样使用 CTest = *CTest::Ins(); 而这样明显是不可以的,还有就是肯定不能允许别人delete掉唯一的ins,所以析构函数应该为private或者protected等等,还有构造函数应该为private等等。

在这里贴出一份比较合理的代码,其实就是我写的load_template.vim插件的一个标准模板。

A.h

#ifndef _X_H_
#define _X_H_
#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;
class CTest
{
    private:
        static auto_ptr<CTest> m_auto_ptr;
        static CTest * m_ins;
    public:
        static CTest * Ins();
    protected:
        CTest();
        CTest(const CTest&);
        virtual ~CTest();
        friend class auto_ptr<CTest>
};
#endif

A.cpp

#include “x.h”
CTest* CTest::m_ins = NULL;
auto_ptr<CTest> CTest::m_auto_ptr;
CTest::CTest()
{
    m_auto_ptr = auto_ptr<CTest>(this);
}
CTest::~CTest()
{
}
CTest* CTest::Ins()
{
    if ( m_ins == NULL)
        m_ins = new CTest();
    return m_ins;
}

暂无相关产品

9则回应给“C/C++正确的构建单体类”

  1. GuoJing说道:

    文章不错,不过我觉得ins还是写全称比较好吧,虽然能理解,然是总在后文感觉不太好读。。可能这是c/c++的习惯吧。。

    [回复]

    Dante 回复:

    呃,看来我简写的有些过火了……呃,要改要改……代码可读性很成问题呢……

    [回复]

  2. thawk说道:

    这种写法没有考虑到在全局或静态变量中使用的问题。如果有人在全局或静态变量中使用CTest,那么在调用CTest::Ins()时,CTest::m_ins可能还没有初始化,这会有问题。
    一个解决方案就是把m_ins移到Ins()中,作为Ins()内部的静态变量,C++标准有规定在进入Ins()时,其内部静态变量一定会被初始化。
    singleton是一个看起来简单,实现起来很复杂的东西 :(

    [回复]

    Dante 回复:

    有道理……,都是static的话确实有初始化先后的问题,我再考虑一下作为内部变量会不会有什么问题~

    [回复]

    alexandercer 回复:

    记得effect c++上,作者就是在ins函数里面使用静态局部变量来实现的.

    [回复]

    Dante 回复:

    ……我家里那本effective c++,早知就好好看看了……
    不过我现在确实改为函数内部实现了,昨天问了一下,确实有个项目组因为这个singleton的static变量放在类外边引发问题的。

    [回复]

    alexandercer 回复:

    事实上呢,不同系统采用了不同的singleton模式,记得orge里面的就是使用模板…ace里面更加的诡异….还是函数内实现最直观方便理解….

    [回复]

  3. jackaldire说道:

    用一个类似MFC的宏来处理更方便
    #define DEFINE_SINGLETON(Singleton)\
    public:\
    static Singleton& Instance();\
    private:\
    Singleton();\
    Singleton(const Singleton&);\
    Singleton& operator=(const Singleton&);\
    ~Singleton();

    #define IMPLEMENT_SINGLETON(Singleton)\
    Singleton & Singleton::Instance() {\
    static Singleton obj;\
    return obj;\
    }

    单件确实是一个想法简单实现困难的东西,即使采用局部静态变量还是有线程安全的问题

    [回复]

    Dante 回复:

    嗯啊,这种方式确实不错哈~~

    [回复]

发表评论