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.