单例模式(Singleton)是用于保证在整个应用程序的生命周期中的任何一个时刻,单例类的实例都只存在一个或者不存在。许多设备管理器常常设计为单例模式,比如很经典的电脑打印程序的设计,一台计算可以链接多台打印机,但是在整个打印过程中只能有一个打印程序的实例,不能让多台打印机打印同一份文件。
单例模式的实现
单例模式属于创建型模式,通过类本身来管理其唯一的实例,唯一的实例是类的一个普通对象。在设计这个类时,必须让他只能创建一个实例并提供对此实例的全局访问。也就是用户不能自己调用类的构造函数进行实例化,因此需要将类的构造函数、析构函数设计为类的私有成员函数。
饿汉模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class CSingleton {
public:
static CSingleton& getInstance()
{
return m_data;
}
private:
static CSingleton m_data;
Singleton(){}
~Singleton(){}
};
CSingleton CSingleton::m_data; //static data member 在类中声明,在类外定义
|
饿汉模式下类在程序开始运行时就直接实例化,而不用等到需要该对象实例时在进行实例化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
// .h
class SA {
public:
static SA& getInstance()
{
return m_data;
}
private:
static SA m_data;
SA();
~SA() {}
};
class SB {
public:
static SB& getInstance()
{
return m_data;
}
void func();
private:
static SB m_data;
SB();
~SB() {}
};
SA SA::m_data;
SB SB::m_data;
// .cpp
SA::SA() {
cout << "this is SA constructor!" << endl;
SB::getInstance().func();
}
SB::SB() {
cout << "this is SB constructor!" << endl;
}
void SB::func()
{
cout << "this is SB!" << endl;
}
// main.cpp
int main() {
return 0;
}
|
程序输出以下结果:
this is SA constructor!
this is SB!
this is SB constructor!
可以看到SB类的成员函数func
先于其构造函数的调用,也就是意味着在类实例化对象之前调用了类的成员函数,这是不安全的。通过分析代码可以知道,在执行到37~38行时候:
1
2
|
SA SA::m_data;
SB SB::m_data; |
会依次调用SA和SB的构造函数,首先调用SA的构造函数:
1
2
3
4
|
SA::SA() {
cout << "this is SA constructor!" << endl;
SB::getInstance().func();
} |
所以首先输出了this is SA constructor!
,而后调用SB的成员函数func
,虽然当前SB尚未定义(还未执行构造函数,即SB SB::m_data
还未执行到),但是SB的类中保存着函数声明,故还是可以调用到SB的成员函数,输出this is SB!
。当完成SA的构造函数后37行代码执行完毕,即SA的单例对象m_data初始化完成。此后将执行第38行SB SB::m_data;
,即执行BS的构造函数,故输出this is SB constructor!
。
通过以上分析可以知道由于SA的构造函数会调用SB的成员函数,因此SB应该先于SA构造,所以可以通过交换第37和38行来实现:
1
2
|
SB SB::m_data;
SA SA::m_data; |
懒汉模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// .h
class Singleton
{
public:
static Singleton* getInstance()
{
if(! m_data) m_data = new Singleton();
return m_data;
}
private:
static Singleton* m_data; //static data member 在类中声明,在类外定义
Singleton(){}
~Singleton(){}
};
//.cpp
Singleton* Singleton::m_data = nullptr;
|
懒汉模式下只有在getInstance()
第一次被调用时才会创建实例,该模式能够保证静态成员的初始化顺序,而且也能够实现多态。但是该模式存在以下问题:
线程不安全
在 static Singleton* getInstance()
方法中,是通过 if 语句判断 静态实例变量 是否被初始化来觉得是否进行初始化,那么在多线程中就有可能出现多次初始化的问题。比方说,有两个多线程同时进入到这个方法中,一个线程运行到if语句进入后还没创建m_data,此时切换到另一线程,m_data的值还是false,同样进入到if语句里初始化变量,那么就会出现两次两次初始化静态实例变量的情况。
内存泄漏
在程序退出时析构函数并未执行,堆中申请的不会得到内存释放,将会导致资源泄露。
改进的懒汉模式
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Singleton {
public:
static Singleton& getInstance() {
static Singleton theSingleton;
return theSingleton;
}
private:
Singleton();
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
~Singleton();
} |
通过使用静态局部变量,可以解决之前的版本的内存泄露的问题,但是这种版本的单例模式仍然存在线程不安全的问题,在局部作用域下的静态变量在编译时,编译器会创建一个附加变量标识静态变量是否被初始化,伪代码如下:
1
2
3
4
5
6
7
8
9
10
|
static Singleton &Instance()
{
static bool constructed = false;
static uninitialized Singleton instance_;
if (!constructed) {
constructed = true;
new(&s) Singleton;
}
return instance_;
} |
当两个线程同时调用instance()时,一个线程运行到if语句进入后还没设constructed值,此时切换到另一线程,constructed值还是false,同样进入到if语句里初始化变量,两个线程都执行了这个单例类的初始化,从而违反了单例模式中实例唯一的原则。可以通过加锁的方式来改进这一问题,但是这样每次调用instance()都要加锁解锁,代价略大。
1
2
3
4
5
6
7
|
static Singleton &getInstance()
{
Lock();
static Singleton instance_;
UnLock();
return instance_;
} |
为了避免频繁加锁解锁可以先对instance_加以判断,为空时再加锁。
boost的解决方案
在前面的方案中:饿汉模式中,使用到了类静态成员变量,但是遇到了初始化顺序的问题; 懒汉模式中,使用到了静态局部变量,但是存在着线程安全等问题。在boost 的实现方式是:单例对象作为静态局部变量,然后增加一个辅助类,并声明一个该辅助类的类静态成员变量,在该辅助类的构造函数中,初始化单例对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
//.h
class SA
{
protected:
struct object_creator
{
object_creator()
{
SA::instance();
}
inline void do_nothing() const {}
};
static object_creator create_object_;
SA();
~SA(){};
public:
static SA *instance()
{
static SA instance;
return &instance;
}
};
SA::object_creator SA::create_object_;
class SB
{
protected:
SB();
~SB(){};
struct object_creator
{
object_creator()
{
SB::instance();
}
inline void do_nothing() const {}
};
static object_creator create_object_;
public:
static SB *instance()
{
static SB instance;
return &instance;
}
void func();
};
SA::object_creator SA::create_object_;
SB::object_creator SB::create_object_;
|
结合饿汉模式中的.cpp文件,可以得到以下正确的输出:
this is SA constructor!
this is SB constructor!
this is SB!
分析程序执行流程可知,首先初始化SA类全局静态变量createobject
->调用object_creator的构造函数
->调用SA::instance()方法初始化单例
->执行SA的构造函数
->调用SB::instance()
->初始化局部静态变量SB instance
->执行SB的构造函数,然后返回这个单例。
boost中的模板实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
template <typename T>
class Singleton
{
struct object_creator
{
object_creator()
{
Singleton<T>::instance();
}
inline void do_nothing() const {}
};
static object_creator create_object;
public:
typedef T object_type;
static T& instance()
{
static T obj;
//这个do_nothing是确保create_object构造函数被调用
//这跟模板的编译有关
create_object.do_nothing();
return obj;
}
};
template <typename T> typename Singleton<T>::object_creator Singleton<T>::create_object;
class QMManager
{
protected:
QMManager() {}
~QMManager() {}
friend class Singleton<QMManager>;
public:
void do_something() {};
};
int main()
{
Singleton<QMManager>::instance().do_something();
return 0;
}
|
boost库通过添加一个类似 proxy-class 的方式,实现了单例模式,但是显然增加了复杂性,在实际应用中应该根据实际情况采用适当的实现方案。
C++11之后的实现方式
1
2
3
4
5
6
|
Singleton* Singleton::m_instance;
Singleton* Singleton::getInstance() {
static std::once_flag oc;//用于call_once的局部静态变量
std::call_once(oc, [&] { m_instance = new Singleton();});
return m_instance;
}
|
或者,c++11标准里static局部变量初始化是线程安全的因此下面这种写法就是正确的,并且非常简洁。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Singleton
{
public:
static Singleton* getInstance()
{
static Singleton instance;//c++11以后都是初始换线程安全的
return &instance;
}
protected:
struct Object_Creator
{
Object_Creator()
{
cout<<"Object_Creator constructor"<<endl;
Singleton::getInstance();
}
};
static Object_Creator _object_creator;
Singleton() {cout<<"Singleton constructor"<<endl;}
~Singleton() {}
};
Singleton::Object_Creator Singleton::_object_creator;
|