查看原文
其他

设计模式之单例模式

程序喵大人 程序喵大人 2022-08-22

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


  什 么 是 单 例 模 式 ?


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

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


  懒 汉 式 实 现 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_;};

总结

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


往期推荐



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存