单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
单例模式中的角色和职责
Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。
如何构建单例:
一是单例模式的类只提供私有的构造函数,
二是类定义中含有一个该类的静态私有对象,
三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
/* 懒汉式 */
class Chairman_lazy
{
private:
Chairman_lazy() {}
public:
static Chairman_lazy* getInstance()
{
if (s_singleton == NULL)
{
s_singleton = new Chairman_lazy;
}
return s_singleton;
}
private:
static Chairman_lazy* s_singleton;
};
Chairman_lazy* Chairman_lazy::s_singleton = nullptr;
void test01()
{
Chairman_lazy* chairman1 = Chairman_lazy::getInstance();
Chairman_lazy* chairman2 = Chairman_lazy::getInstance();
if (chairman1 == chairman2)
{
cout << "指向同一个对象!" << endl;
}
else
{
cout << "指向不是同一个对象!" << endl;
}
}
/* 饿汉式 */
class Chairman_hangry
{
private:
Chairman_hangry() {}
public:
static Chairman_hangry* getInstance()
{
return s_singleton;
}
private:
static Chairman_hangry* s_singleton;
};
//初始化
Chairman_hangry* Chairman_hangry::s_singleton = new Chairman_hangry;
void test02() {
Chairman_hangry* chairman1 = Chairman_hangry::getInstance();
Chairman_hangry* chairman2 = Chairman_hangry::getInstance();
if (chairman1 == chairman2) {
cout << "指向同一个对象!" << endl;
}
else {
cout << "指向不是同一个对象!" << endl;
}
}
int main() {
test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
注意:静态变量必须在类外进行初始化,否则编译不过
饿汉式还可以使用静态变量得方式:
class Lazy
{
private:
Lazy() {}
public:
static Lazy* getInstance()
{
static Lazy lazy;
return &lazy;
}
};
int main()
{
Lazy *pLazy = Lazy::getInstance();
Lazy *pLazy2 = Lazy::getInstance();
cout << pLazy << endl;
cout << pLazy2 << endl;
return 0;
}
单例碰到多线程
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<Windows.h>
using namespace std;
/* 懒汉式 */
class Chairman_lazy
{
private:
Chairman_lazy() {}
public:
static Chairman_lazy* getInstance()
{
if (s_singleton == NULL) {
//Sleep(1000); //等到1000秒
s_singleton = new Chairman_lazy;
}
return s_singleton;
}
private:
static Chairman_lazy* s_singleton;
};
Chairman_lazy* Chairman_lazy::s_singleton = NULL;
/* 饿汉式 */
class Chairman_hangry
{
private:
Chairman_hangry() {}
public:
static Chairman_hangry* getInstance()
{
return s_singleton;
}
private:
static Chairman_hangry* s_singleton;
};
//初始化
Chairman_hangry* Chairman_hangry::s_singleton = new Chairman_hangry;
DWORD WINAPI MyThread_hangry(LPVOID lpThreadParameter)
{
Chairman_hangry* chairman = Chairman_hangry::getInstance();
cout << "单例对象地址:" << (int*)chairman << endl;
return 0;
}
//饿汉式单例碰到多线程测试
void test01()
{
HANDLE handler[10];
for (int i = 0; i < 10; i++)
{
handler[i] = CreateThread(NULL, NULL, MyThread_hangry, NULL, NULL, NULL);
}
}
DWORD WINAPI MyThread_lazy(LPVOID lpThreadParameter)
{
Chairman_lazy* chairman = Chairman_lazy::getInstance();
cout << "单例对象地址:" << (int*)chairman << endl;
return 0;
}
//懒汉式单例碰到多线程
void test02()
{
HANDLE handler[10];
for (int i = 0; i < 10; i++)
{
handler[i] = CreateThread(NULL, NULL, MyThread_lazy, NULL, NULL, NULL);
}
}
int main() {
//test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
Test01函数执行结果(饿汉式单例模式):
Test02函数执行结果(懒汉式单例模式):
从结果可以看出,饿汉式多线程环境没有问题,而懒汉式多线程环境会存在问题,主要时由于在判断对象为空时可能多个线程都进来了导致多次new对象,解决这种方式的问题就是加锁。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<Windows.h>
#include <mutex>
using namespace std;
/* 懒汉式 */
class Chairman_lazy
{
private:
Chairman_lazy() {}
public:
static Chairman_lazy* getInstance()
{
if (s_singleton)
return s_singleton;
// 双重检查锁定
if (s_singleton == NULL) {
m_mutex.lock();
if (s_singleton == NULL) {
//Sleep(1000); //等到1000秒
s_singleton = new Chairman_lazy;
}
m_mutex.unlock();
}
return s_singleton;
}
private:
static Chairman_lazy* s_singleton;
static mutex m_mutex;
};
Chairman_lazy* Chairman_lazy::s_singleton = NULL;
mutex Chairman_lazy::m_mutex;
单例模式的优缺点
优点:
(1)单例模式提供了对唯一实例的受控访问。
(2)节约系统资源。由于在系统内存中只存在一个对象。
缺点:
(1) 扩展略难。单例模式中没有抽象层。
(2) 单例类的职责过重。