Cpp设计模式之单例模式

C++常用设计模式之单例模式

序言

单例模式顾名思义,保证一个类在内存中仅有一个实例化对象,并提供一个可以访问它的全局化接口。实现单例模式必须注意以下几点:

  1. 单例类只能有一个实例化对象。
  2. 单例类必须自己提供一个实例化对象。
  3. 单例类必须提供一个可以访问唯一实例化对象的接口。
    单例模式分为懒汉式和饿汉式两种实现方式。

懒汉单例模式

懒汉,顾名思义,不到万不得已就不会去实例化类,也就是说第一次用到类实例的时候才会去实例化一个对象。在访问量较小,甚至可能不会去访问的情况下,采用懒汉实现,以时间换空间。

非线程安全的懒汉单例模式

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
/*
关键代码:构造函数私有,不能通过赋值运算,拷贝构造等方法实例化对象
*/

//懒汉式一般实现:非线程安全,getInstance返回的实例指针需要delete
class SingleMan
{
public:
static SingleMan *getInstance();
~SingleMan(){}

private:
SingleMan(){} //不允许类外实例化
SingleMan(const SingleMan& man) = delete; //不允许拷贝构造
SingleMan& operator=(const SingleMan& man) = delete; //不允许赋值

static SingleMan* m_singleMan;
};
SingleMan *SingleMan::m_singleMan = nullptr;

SingleMan *SingleMan::getInstance()
{
if(m_singleMan == nullptr)
{
m_singleMan = new SingleMan();
}
return m_m_singleMan;
}

非线程安全的懒汉单例模式

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
class SingleMan
{
public:
static SingleMan *getInstance();
~SingleMan(){}

private:
SingleMan(){} //不允许类外实例化
SingleMan(const SingleMan& man) = delete; //不允许拷贝构造
SingleMan& operator=(const SingleMan& man) = delete; //不允许赋值

static SingleMan* m_singleMan;

std::mutex m_lock;
};
SingleMan *SingleMan::m_singleMan = nullptr;

SingleMan *SingleMan::getInstance()
{
if(m_singleMan == nullptr)
{
m_lock.lock();
if(m_singleMan == nullptr)
{
m_singleMan = new SingleMan();
}
m_lock.unlock();
}
return m_m_singleMan;
}

返回一个reference指向local static对象

这种单例模式实现方式多线程可能存在不确定性:任何一种non-const static 对象,不论它是local还是non-local,在多线程环境下”等待某事发生“都会有麻烦。
解决办法:在程序的单线程启动阶段手工调用所有reference-returing函数。
下面这种实现方式的好处就是你不需要去关心实例的释放,因为static修饰的变量生命周期和程序一样长。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SingleMan
{
public:
static SingleMan& getInstance();

private:
SingleMan(){} //不允许类外实例化
SingleMan(const SingleMan& man) = delete; //不允许拷贝构造
SingleMan& operator=(const SingleMan& man) = delete; //不允许赋值

};

SingleMan& SingleMan::getInstance()
{
static SingleMan singleMan;
return singleMan;
}

饿汉单例模式

饿汉模式,在访问量较大,或者访问线程较多时,采用饿汉实现,可以实现更好的性能。这是一种空间换时间的策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//饿汉式:线程安全,注意一定要在合适的地方去delete它
class SingleMan
{
public:
static SingleMan* getInstance();
private:
SingleMan(){} //不允许类外实例化
SingleMan(const SingleMan& man) = delete; //不允许拷贝构造
SingleMan& operator=(const SingleMan& man) = delete; //不允许赋值

static SingleMan* m_singleMan;
};

SingleMan* SingleMan::m_singleMan = new SingleMan();

SingleMan* SingleMan::getInstance()
{
return m_singleMan;
}

UML类图:
img not found