“对象性能”模式
面向对象很好地解决了“抽象”的问题,但是必不可免地要付出一定的代价。对于通常情况来讲,面向对象的成本大都可以忽略不计。但是某些情况,面向对象所带来的成本必须谨慎处理。
经典模式:Singleton、Flyweight
动机(Motivation)
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确,以及良好的效率。
如何绕过常规的构造汽车,提供一种机制来保证一个类只有一个实例?
这应该是类设计者的责任,而不是使用者的责任。
实例1:
#pragma once
#include <mutex>
class Singleton {
private:
Singleton();
Singleton(const Singleton& other);
public:
static Singleton* getInstance();
static Singleton* instance_;
private:
static std::mutex mutex_;
};
Singleton* Singleton::instance_ = nullptr;
/*
//线程不安全版本
Singleton* Singleton::getInstance() {
if (nullptr == instance_)
{
instance_ = new Singleton;
}
return instance_;
}
*/
/*
//线程安全版本1(性能不高)
Singleton* Singleton::getInstance() {
std::lock_guard<std::mutex> locker(mutex_);
if (nullptr == instance_)
{
instance_ = new Singleton;
}
return instance_;
}
*/
//线程安全版本2(double check)
Singleton* Singleton::getInstance() {
if (nullptr == instance_)
{
std::lock_guard<std::mutex> locker(mutex_);
if (nullptr == instance_)
{
instance_ = new Singleton;
}
}
return instance_;
}
int mian()
{
Singleton* singleton = Singleton::getInstance();
}
实例2:(双检查锁,并解决内存读写reorder不安全问题版本)
#pragma once
#include <mutex>
#include <atomic>
class Singleton {
private:
Singleton();
Singleton(const Singleton& other);
public:
static Singleton* getInstance();
static std::atomic<Singleton*> instance_;
private:
static std::mutex mutex_;
};
std::atomic<Singleton*> Singleton::instance_ = nullptr;
std::mutex Singleton::mutex_;
//C++11之后的跨平台版本(volatile)
Singleton* Singleton::getInstance() {
Singleton* tmp = instance_.load(std::memory_order_relaxed);
std::_Atomic_thread_fence(std::memory_order_acquire);//获取内存fence
if (nullptr == tmp) {
std::lock_guard<std::mutex> locker(mutex_);
if (nullptr == tmp) {
tmp = new Singleton();
std::_Atomic_thread_fence(std::memory_order_release);
instance_.store(tmp);
}
}
return tmp;
}
int mian()
{
Singleton* singleton = Singleton::getInstance();
}
模式定义
保证一个类仅有一个实例,并提供一个该实例的全局访问点。
要点总结
Singletonm模式中实例构造器可以设计为protected以允许子类派生。
Singleton模式一般不要支持拷贝构造函数和Clone接口,因为这有可能导致多个对象实例,与Singleton模式的初衷违背。
如何实现多线程环境下安全的Singleton?注意对双向检查锁的正确实现。