alin的学习之路:设计模式之单例模式
单例模式,顾名思义就是类只有一个实例对象,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式的优点:
- 在内存中只有一个对象, 节省内存空间
- 避免频繁的创建销毁对象,可以提高性能
- 避免对共享资源的多重占用
- 可以全局访问
c++开发的时候, 一般情况下不允许使用全局变量, 因为这样会破坏类的封装, 写一个单例模式的类来代替全局变量
1. 如何实现一个单例
/*
保证类的对象只有一个:
- 创建类对象使用的构造函数 -> 需要将构造变为私有函数
- 可以基于一个对象拷贝出另一个对象 -> 需要将拷贝构造函数变为私有
- 不能在外部调用这个构造和拷贝构造, 对象从哪儿来呢? -> 需要在类的内部通过构造函数创建出一个实例对象
- 类的成员变量不允许直接在类外部访问 -> 私有成员变量
- 私有成员变量需要提供一个公共成员函数进行访问 -> 普通的类成员函数必须通过类对象来调用
- 现在没有对象(在类的外部无法创建), 因此需要通过类名来调用这个成员函数 -> 这种函数是静态函数
- 需要在静态函数来访问私钥的类对象 -> 在静态函数中只能访问静态的成员变量 -> 类的对象也是静态的
*/
class Test
{
public:
// 构造函数
// 默认析构函数
// 拷贝构造函数
}
// 保证构造和拷贝构造不能被外部调用, 所有应该设置他们的属性为私有的
class Test
{
public:
// 默认析构函数
static Test* getInstance()
{
return &m_t;
}
private:
// 构造函数
Test();
// 拷贝构造函数
Test(const Test &t);
// 提供一个唯一的实例对象, 属于类
// 这个对象是私有的不能直接访问, 而且的静态, 需要提供一个静态函数进行对象的访问
static Test m_t;
}
// 实现单例类的方式
// 1. 懒汉模式: 单例对象只有被使用的时候才被创建
// 2. 饿汉模式: 单例对象在没有被使用之前被创建
2. 懒汉模式
懒汉模式,顾名思义就是
实例在用到的时候才去创建
,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。使用过程中有线程安全的问题
。
- 加锁
//
// test.cpp
//
Mutex mutex;
Test* Test::m_test = NULL;
Test* Test::getInstance()
{
// 如果对象已经存在了, 多个线程访问单例对象是线性的, 不是并行的
// 不管单例对象存在还是不存在, 对对象的访问都是线性的(顺序访问), 效率低
// 如果对象是存在的, 对象是可以并行访问的(优化思路)
mutex.lock();
if(m_test == NULL)
{
m_test = new Test();
}
m_mutex.unlock();
return m_test;
}
- 双重检查和锁搭配使用
// 版本3: 提高效率, 提高了第二波(包括2)后边线程的访问效率
// 双重检查锁
//
// test.cpp
//
Mutex mutex;
Test* Test::m_test = NULL;
Test* Test::getInstance()
{
// 如果单例对象已经存在, 直接并行访问单例对象
// 如果单例对象不存在, 多个线程进入到第一个if中, 顺序访问互斥锁
if(m_test == NULL)
mutex.lock();
if(m_test == NULL)
{
m_test = new Test();
}
m_mutex.unlock();
}
return m_test;
}
- 使用c++的语法中的线程同步的机制
// 版本4: 要求编译器必须支持c++11
//
// test.h
//
class Test
{
public:
static Test* getInstance();
private:
// 构造函数
Test();
// 拷贝构造函数
Test(const Test &t);
}
//
// test.cpp
//
Test* Test::getInstance()
{
// 静态的局部变量
static Test* m_test = new Test(); //底层已经实现了线程的同步,不需要再考虑
return m_test;
}
3. 饿汉模式
饿汉模式,从名字上也很好理解,就是“比较勤”,
实例在初始化的时候就已经建好了
,不管你有没有用到,都先建好了再说。好处是没有线程安全的问题,坏处是浪费内存空间
。推荐的方式。
// 方式1
//
// test.h
//
class Test
{
public:
// 默认析构函数
static Test* getInstance();
private:
// 构造函数
Test();
// 拷贝构造函数
Test(const Test &t);
static Test *m_test;
}
//
// test.cpp
//
// 实例化单例对象
Test* Test::m_test = new Test();
Test* Test::getInstance()
{
return m_test;
}
// 方式2
//
// test.h
//
class Test
{
public:
// 默认析构函数
static Test* getInstance();
private:
// 构造函数
Test();
// 拷贝构造函数
Test(const Test &t);
static Test m_test;
}
//
// test.cpp
//
// 对象已经被实例化了, 在外部声明一下这个变量就可以了
Test Test::m_test;
Test* Test::getInstance()
{
return &m_test;
}