### 使用c++11改进我们的模式 ### 8.1 改进单例模式 单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。 ``` #pragma once template class Singleton { public: template   static T* Instance(Args&&... args)   { if(m_pInstance==nullptr) m_pInstance = new T(std::forward(args)...); return m_pInstance; }   static T* GetInstance()   {     if (m_pInstance == nullptr)       throw std::logic_error("the instance is not init, please initialize the instance first");     return m_pInstance;   } static void DestroyInstance() { delete m_pInstance; m_pInstance = nullptr; } private: Singleton(void); virtual ~Singleton(void); Singleton(const Singleton&); Singleton& operator=(const Singleton&); private: static T* m_pInstance; }; template T* Singleton::m_pInstance = nullptr; ``` 使用了可变参数与完美转发,避免了写N个模板函数 单例模式应用的场景一般发现在以下条件下: 1. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 2. 控制资源的情况下,方便资源之间的互相通信。如线程池等。 3. 只需一个存在就可以的情况 ### 8.2 改进观察者模式 观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都能得到通知并被自动更新。 ``` #pragma once class NonCopyable { public: NonCopyable(const NonCopyable&) = delete; // deleted NonCopyable& operator = (const NonCopyable&) = delete; // deleted NonCopyable() = default; // available }; ``` ``` #include #include #include #include using namespace std; #include "NonCopyable.hpp" template class Events : NonCopyable { public: //注册观察者,支持右值引用 int Connect(Func&& f) { return Assgin(f); } //注册观察者 int Connect(const Func& f) { return Assgin(f); } //移除观察者 void Disconnect(int key) { m_connections.erase(key); } //通知所有的观察者 template void Notify(Args&&... args) { for (auto& it: m_connections) { it.second(std::forward(args)...); } } private: //保存观察者并分配观察者的编号 template int Assgin(F&& f) { int k=m_observerId++; m_connections.emplace(k, std::forward(f)); return k; } int m_observerId=0;//观察者对应的编号 std::map m_connections;//观察者列表 }; ``` ### 8.3 改进访问者模式 访问者模式需要注意的问题:定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重新定义对所有访问者的接口。
通过可变参数模板就可以实现一个稳定的接口层,可以让访问者的接口层访问任意个数的访问者,这样就不需要每增加一个新的被访问者就修改接口层,从而使接口层保证稳定。 ``` template struct Visitor; template struct Visitor : Visitor { using Visitor::Visit;//通过using避免隐藏基类的visit同名方法 virtual void Visit(const T&) = 0; }; template struct Visitor { virtual void Visit(const T&) = 0; }; ``` ### 8.4 改进命令模式 命令模式的作用是将请求封装成一个对象,将请求的发起者和执行者解耦,支持对请求排队、撤销和重做。由于将请求都封装成一个一个命令对象了,使得我们可以集中处理或者延迟处理这些命令请求,而且不同客户对象可以共享这些命令,还可以控制请求的优先权、排队、支持请求的撤销和重做。
缺点:越来越多的命令会导致类爆炸,难以管理
接收function、函数对象、lambda和普通函数的包装器: ``` template::value>::type> void Wrap(F&& f, Args && ...args) { return f(std::forward(args)...); } ``` 接收成员函数的包装器: ``` template void Wrap(R(C::*f)(DArgs...),P&& p,Args&& ... args) { return; (*p.*f)(std::forward(args)...); } ``` ``` #include #include template struct CommCommand { private: std::function < Ret()> m_f; public: //接受可调用对象的函数包装器 template< class F, class... Args, class = typename std::enable_if::value>::type> void Wrap(F && f, Args && ... args) { m_f = [&]{return f(args...); }; } //接受常量成员函数的函数包装器 template void Wrap(R(C::*f)(DArgs...) const, P && p, Args && ... args) { m_f = [&, f]{return (*p.*f)(args...); }; } //接受非常量成员函数的函数包装器 template void Wrap(R(C::*f)(DArgs...), P && p, Args && ... args) { m_f = [&, f]{return (*p.*f)(args...); }; } Ret Excecute() { return m_f(); } }; ``` ### 8.5 改进对象池 ``` #include #include #include #include #include "NonCopyable.hpp" using namespace std; const int MaxObjectNum = 10; template class ObjectPool : NonCopyable { template using Constructor = std::function(Args...)>; public: //默认创建多少个对象 template void Init(size_t num, Args&&... args) { if (num<= 0 || num> MaxObjectNum) throw std::logic_error("object num out of range."); auto constructName = typeid(Constructor).name(); //不区分引用 for (size_t i = 0; i (new T(std::forward(args)...), [this, constructName](T* p) //删除器中不直接删除对象,而是回收到对象池中,以供下次使用 { m_object_map.emplace(std::move(constructName), std::shared_ptr(p)); })); } } //从对象池中获取一个对象 template std::shared_ptr Get() { string constructName = typeid(Constructor).name(); auto range = m_object_map.equal_range(constructName); for (auto it = range.first; it != range.second; ++it) { auto ptr = it->second; m_object_map.erase(it); return ptr; } return nullptr; } private: multimap> m_object_map; }; ```