一、设计原则
- 依赖倒置原则
- 高层模块不应该依赖底层模块,二者都应该依赖抽象
- 抽象不应该依赖具体实现,具体实现应该依赖与抽象
- 开放封闭原则
- 一个类应该对扩展开放,对修改关闭
- 面向接口编程
- 不将变量类型声明为某个特定的具体类,而是声明为某个接口
- 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口
- 减少系统中各部分的依赖关系,从而实现“高内聚,低耦合”的设计方案
- 封装变化点
- 将稳定点和变化点分离,扩展修改变化点,让稳定点与变化点的实现层次分离
- 单一职责原则
- 一个类应该仅有一个引起它变化的原因
- 里氏替换原则
- 子类必须能够替换它的父类型;
- 错误:主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没实现父类方法的职责
- 接口隔离原则
- 不应该强迫客户依赖于他们不用的方法
- 一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责
- 对象组合优先类继承
- 继承耦合度高,组合耦合度低
二、单例模式
1.定义
保证⼀个类仅有⼀个实例,并提供⼀个该实例的全局访问点
2.代码
版本1:有内存泄露和线程不安全的问题
class Singleton {
public:
static Singleton * GetInstance() {
if (_instance == nullptr) {
_instance = new Singleton();
}
return _instance;
}
private:
Singleton(){}//构造
Singleton(const Singleton &clone){} //拷⻉构造
Singleton& operator=(const Singleton&) {}
static Singleton * _instance;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
版本2:// 还可以使⽤ 内部类,智能指针来解决; 此时还有线程安全问
class Singleton {
public:
static Singleton * GetInstance() {
if (_instance == nullptr) {
_instance = new Singleton();
atexit(Destructor);
}
return _instance;
}
~Singleton() {}
private:
static void Destructor() {
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
}
}
Singleton();//构造
Singleton(const Singleton &cpy); //拷⻉构造
Singleton& operator=(const Singleton&) {}
static Singleton * _instance;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
版本3.1:粒度太大
版本3.2:也有问题,new + 赋值 不是原子操作 第一个_instance可能不为空 但是还没有创建出来
#include <mutex>
class Singleton { // 懒汉模式 lazy load
public:
static Singleton * GetInstance() {
//std::lock_guard<std::mutex> lock(_mutex); // 3.1 切换线程
if (_instance == nullptr) {
std::lock_guard<std::mutex> lock(_mutex); // 3.2
if (_instance == nullptr) {
_instance = new Singleton();
atexit(Destructor);
}
}
return _instance;
}
private:
static void Destructor() {
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
}
}
Singleton(){} //构造
Singleton(const Singleton &cpy){} //拷⻉构造
Singleton& operator=(const Singleton&) {}
static Singleton * _instance;
static std::mutex _mutex;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
版本4:内存屏障解决方案
#include <mutex>
#include <atomic>
class Singleton {
public:
static Singleton * GetInstance() {
Singleton* tmp = _instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);//获取内存屏障
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(_mutex);
tmp = _instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存屏障
_instance.store(tmp, std::memory_order_relaxed);
atexit(Destructor);
}
}
return tmp;
}
private:
static void Destructor() {
Singleton* tmp = _instance.load(std::memory_order_relaxed);
if (nullptr != tmp) {
delete tmp;
}
}
Singleton(){}
Singleton(const Singleton&) {}
Singleton& operator=(const Singleton&) {}
static std::atomic<Singleton*> _instance;
static std::mutex _mutex;
};
std::atomic<Singleton*> Singleton::_instance;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
// g++ Singleton.cpp -o singleton -std=c++11
版本5:C++11 magic static 特性:如果当变量在初始化的时候,并同时进入声明语句,并发线程将会阻塞等待初始化结束
class Singleton
{
public:
~Singleton(){}
static Singleton& GetInstance() {
static Singleton instance;
return instance;
}
private:
Singleton(){}
Singleton(const Singleton&) {}
Singleton& operator=(const Singleton&) {}
};
版本6:居于版本5修改的模板类
template<typename T>
class Singleton {
public:
static T& GetInstance() {
static T instance; // 这⾥要初始化DesignPattern,需要调⽤DesignPattern 构造函数,同时会调⽤⽗类的构造函数。
return instance;
}
protected:
virtual ~Singleton() {}
Singleton() {} // protected修饰构造函数,才能让别⼈继承
Singleton(const Singleton&) {}
Singleton& operator =(const Singleton&) {}
};
class DesignPattern : public Singleton<DesignPattern> {
friend class Singleton<DesignPattern>; // friend 能让 Singleton<T> 访问到 DesignPattern构造函数
private:
DesignPattern(){}
DesignPattern(const DesignPattern&) {}
DesignPattern& operator=(const DesignPattern&) {}
}
总结
C++ magic static 完美解决单例模式的内存泄漏和线程安全问题