C++类模板实现单例模式与资源自动释放

Posted by XP on May 11, 2019

C++类模板实现单例模式与资源自动释放


本编笔记主要记录最近项目中遇到的问题–单例模式中的自动资源释放没有运行,因此探究了类模板实现的单例模式中的静态变量的使用问题,主要包括:

  • 单例模式的资源自动回收

  • 类模板实现的单例模式遇到的问题

  • 解决资源不能自动释放方法

1,单例模式的资源自动回收

为了实现在程序结束时,将单例中的instance析构掉,我们可以在单例中定义一个内部类Garbo,并为单例类增加一个静态属性garbo,

代码如下:

class NaiveSingleton
{
public:
    static NaiveSingleton& getInstance()
    {
        if (__instance == nullptr)
        {
            __instance = new NaiveSingleton;
        }

        return *__instance;
    }

    void fun() { cout << "NaiveSingleton fun" << endl;}

protected:
    NaiveSingleton() { cout << "NaiveSingleton()" << endl; }
    ~NaiveSingleton() { cout << "~NaiveSingleton()" << endl; }

private:
    static NaiveSingleton *__instance;

    static class Garbo
    {
    public:
        Garbo() { cout << "this is Carbo()" << endl;}
        ~Garbo()
        {
            cout << "delete Instance" << endl;
            delete __instance;
            __instance = nullptr;
        }
    } __garbo;
};

NaiveSingleton* NaiveSingleton::__instance;
NaiveSingleton::Garbo NaiveSingleton::__garbo;

int main()
{
    cout << "\n-------------START--------------->\n";

    NaiveSingleton::getInstance().fun();

    cout << "\n------------END---------------->\n";
    return 0;
}

简单起见,上述代码没有考虑多线程,去除了锁的逻辑。程序结束时,静态属性__garbo会被系统自动析构,在析构__garbo时,会调用到析构函数~Garbo,此时delete __instance. 代码输出如下:

this is Carbo()   //初始化静态变量__garbo,调用了构造函数

---------------------------->

NaiveSingleton()     //NaiveSingleton::getInstance() 构造 NaiveSingleton实例

NaiveSingleton fun   // 调用fun函数  

------------END---------------->

delete Instance      // 程序结束,析构__garbo

~NaiveSingleton()    // 析构 NaiveSingleton 实例

2,类模板实现的单例模式遇到的问题

在项目中,我们把单例模式写成了类模板,每一个需要成为单例的类,则继承自该类模板,实际使用时,发现__garbo并未被析构,导致资源没有被释放。代码如下:

//"TSingleton.h"

#ifndef _TEMPLATE_SINGLETON_H_

#define _TEMPLATE_SINGLETON_H_

#include <iostream>

using namespace std;


template<typename TYPE>
class TSingleton
{
public:
        static TYPE& getInstance() {
                if(__instance == nullptr) {
                        __instance = new TYPE;
                }
                return *__instance;
        }

protected:
        TSingleton() { cout << "TSingleton constuctor" << endl; };
        virtual ~TSingleton() { cout << "TSingleton distructor" << endl;}

private:
        TSingleton(const TSingleton& obj);
        TSingleton& operator=( const TSingleton& obj);

private:
        static TYPE* __instance;

        /* auto free allocated memory when process terminate */
        class Garbo {
            public:
                Garbo() { cout << "this is Garbo" << endl;}
                ~Garbo() {
                    cout << "this is~Garbo" << endl;
                    if (TSingleton<TYPE>::__instance != nullptr) {
                        cout << "Delete intance in Garbo" << endl;
                        delete (TSingleton<TYPE>::__instance);
                        TSingleton<TYPE>::__instance = nullptr;
                    }
                }
        };

        static Garbo __garbo;
};


template<typename TYPE>  
TYPE* TSingleton<TYPE>::__instance = nullptr;

template<typename TYPE>  
typename TSingleton<TYPE>::Garbo TSingleton<TYPE>::__garbo;


#endif

对于需要设计成单例的类,我们直接继承TSingleton, 代码如下:

//singletonTest1.h

#ifndef _SINGLETON_TEST_1_H_

#define _SINGLETON_TEST_1_H_

#include "TSingleton.h"


class Test1 : public TSingleton<Test1>
{
public:
        Test1() { cout << "this is Test1()" << endl;}
        ~Test1() { cout << "this is ~Test1()" << endl;}

        void fun1() { cout << "this is Test1::fun1" << endl;}

        friend class TSingleton<Test1>;
};

#endif

测试代码如下:

#include "singletonTest1.h"

int main()
{

    cout << "\n---------------------------->\n";
    Test1::getInstance().fun1();
    cout << "\n------------END---------------->\n";
    return 0;

}

运行程序,结果如下:

---------------------------->
TSingleton constuctor
this is Test1()
this is Test1::fun1

------------END---------------->

可以看出,静态变量__garbo并没有初始化,最终程序结束时,析构函数也不会被调用,因此instance没有被delete,为什么最开始的不是模板的情况下,__garbo可以被初始化,完成资源释放的功能,写成模板之后就失效了呢?

3,解决资源不能自动释放方法

对于类模板中的静态成员,在代码中没有任何使用到的地方时,编译器并不会隐式地实例化,所以虽然我们在代码中写了 typename TSingleton<TYPE>::Garbo TSingleton<TYPE>::__garbo; 但是,因为__garbo没有在其它地方被使用,因此没有实际的实例化;为了强迫编译器实例化__garbo, 我们可以在代码中加入 (void)&__garbo , 之后Garbo就能生效了,修改如下:

    TSingleton() {  

        cout << "TSingleton constuctor" << endl;
        //Taking the address forces the instantiation of the static variable __garbo
        
        (void)&__garbo;
    };

输出如下:

this is Garbo

---------------------------->
TSingleton constuctor
this is Test1()
this is Test1::fun1

------------END---------------->
this is~Garbo
Delete intance in Garbo
this is ~Test1()
TSingleton distructor

关于类模板中的静态变量叙述可以参考这里

14.7.1p8 The implicit instantiation of a class template does not cause any static data members of that class to be implicitly instantiated.

“Unless a member of a class template […] has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.

然鹅~~ 吭哧吭哧写到这里,发现C++11中单例的标准做法已经是:

static Singleton& get() {
  static Singleton instance;
  return instance;
}

详情参考这里

C++11 removes the need for manual locking. Concurrent execution shall wait if a static local variable is already being initialized.

§6.7 [stmt.dcl] p4

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.