当前位置:首页 > 公众号精选 > 程序喵大人
[导读]程序喵最近开始系统学习回顾设计模式,希望能把学到的东西分享给大家,今天总结下创建型模式中最经典的单例模式。 “    什 么 是 单 例 模 式 ?  ” 这里首先介绍下什么是创建型模式,创建型模式主要解决对象的创建过程,封装对象复杂的创建过程,解耦对象


程序喵最近开始系统学习回顾设计模式,希望能把学到的东西分享给大家,今天总结下创建型模式中最经典的单例模式。


  
什 么 是 单 例 模 式 ?


 ”
这里首先介绍下什么是创建型模式,创建型模式主要解决对象的创建过程,封装对象复杂的创建过程,解耦对象的创建代码和对象的使用代码。
而单例模式指的就是在系统里一个类只能产生一个实例,确保全局唯一。

怎么实现单例模式呢,常见的有以下几种方法,这里程序喵只介绍线程安全的实现:


  懒 汉 式 实 现 1

 ”

直接看代码:
#include <iostream>#include <mutex>
class Singleton { public: static Singleton *GetInstance();
void Func() { std::cout << "Singleton Func \n"; }
private: Singleton() {} // 避免外部构造对象,想拿到类的实例只能通过GetInstance() Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;
static Singleton *instance_; static std::mutex mutex_;};
Singleton *Singleton::instance_ = nullptr;std::mutex Singleton::mutex_;
Singleton *Singleton::GetInstance() { // double check if (instance_ == nullptr) { // 1 std::unique_lock<std::mutex> lock(mutex_); if (instance_ == nullptr) { // 2 instance_ = new Singleton(); } } return instance_;}
int main() { Singleton::GetInstance()->Func(); return 0;}
这就是传说中的 double check 方法,这里有几个关键点:

1

为什么需要加锁?加锁的原因很简单,为了确保线程安全。

2

您看过代码可能也有些疑惑,明明加一次锁和第二次的判断就足够了,为什么在加锁之前还需要进行一次判断呢?这里可以考虑只需要在判断指针为空的时候才去加锁,避免每次调用方法时都加锁,可以减少加锁解锁带来的额外开销。

3

这里需要将类的构造函数和拷贝构造函数等设成私有函数,避免外部构造类的实例,防止外部通过new关键字进行实例化。

  懒 汉 式 实 现 2
 ”

通过局部静态变量的方式实现,这种方法在C++11后是线程安全的,见代码
#include <iostream>#include <mutex>
class Singleton { public: static Singleton& GetInstance() { static Singleton instance; return instance; }
void Func() { std::cout << "Singleton Func \n"; }
private: Singleton() {} // 避免外部构造对象,想拿到类的实例只能通过GetInstance() Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;
};
int main() { Singleton::GetInstance().Func(); return 0;}
C++11确保局部静态变量创建时线程安全的,本人基本上用的单例都是使用这种方式。

  饿 汉 式 实 现
 ”

懒汉式指的是在需要实例的时候才去创建,就是上面的方法,而饿汉式则是提前创建好实例,外部需要实例的时候直接获取这个已经创建好的实例,看下饿汉式单例的实现吧:
#include <iostream>
class Singleton { public: static Singleton *GetInstance();
void Func() { std::cout << "Singleton Func \n"; }
private: Singleton() {} // 避免外部构造对象,想拿到类的实例只能通过GetInstance() Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;
static Singleton *instance_;};
Singleton *Singleton::instance_ = new Singleton();
Singleton *Singleton::GetInstance() { return instance_;}
int main() { Singleton::GetInstance()->Func(); return 0;}

  std::call_once实现

在C++11后可以利用新特性实现线程安全的单例模式,代码如下:
template <typename T>class SingleTon {public: static T& instance() { std::call_once(once_, &SingleTon::init); return *value_;}
private: SingleTon(); ~SingleTon();
SingleTon(const SingleTon&) = delete; SingleTon& operator=(const SingleTon&) = delete;
static void init() { value_ = new T(); } static T* value_;
static std::once_flag once_;};

总结

单例模式是一个经典常用的设计模式,在面试过程中也经常会被问到,当整个进程需要使用唯一的实例时,可以考虑使用单例模式,单例模式有几个关键点:
  • 类只能有一个实例。
  • 外部用户不能也无法自行对类进行实例化,实例化的操作需要类内部去实现。
  • 整个进程共用一个类的实例,且需要是线程安全的。


往期推荐



免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
关闭
关闭