# 单例模式 ## 介绍 单例模式理解起来比较简单, 就是一个类 它只允许创建一个对象或者实例, 那这个类就是单例类. ## 用处 在业务上, 有些数据只需要保存一份, 比如系统信息, 配置信息 1. 可以节省空间、资源 2. 方便控制, 避免多对象引起复杂操作 单例有下面两种经典的实现方式。 ## 饿汉式 (线程安全) 它在程序启动时立即创建单例对象,而不是在首次访问时才创建。 这确保了线程安全,因为在多线程环境下,单例对象已经在程序启动阶段被创建,不需要考虑竞态条件。 使用普通静态变量方式 ```cpp class HungryManSingleton { public: static HungryManSingleton& getInstance() { // 静态成员变量确保只有一个实例 static HungryManSingleton instance; return instance; } ~HungryManSingleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } // 其他成员函数和数据成员 private: // 私有构造函数,防止外部实例化 HungryManSingleton() { // 初始化单例对象 std::cout << __PRETTY_FUNCTION__ << std::endl; } // 禁用拷贝构造函数 HungryManSingleton(const HungryManSingleton&) = delete; // 禁用赋值操作符 HungryManSingleton& operator=(const HungryManSingleton&) = delete; }; ``` 使用c++11 `std::call_one` 方式, 是 C++ 标准库提供的一个多线程同步工具,用于确保某个函数只会在多线程环境下执行一次。通常,它与 **`std::once_flag`** 一起使用 ```cpp class HungryManCallOneSingleton { public: static std::shared_ptr getInstance() ; ~HungryManCallOneSingleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } private: HungryManCallOneSingleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } // 禁用拷贝构造函数 HungryManCallOneSingleton(const HungryManCallOneSingleton&) = delete; // 禁用赋值操作符 HungryManCallOneSingleton& operator=(const HungryManCallOneSingleton&) = delete; }; static std::shared_ptr singleton = nullptr; static std::once_flag singletonFlag; std::shared_ptr HungryManCallOneSingleton::getInstance() { std::call_once(singletonFlag, [&] { singleton = std::shared_ptr(new HungryManCallOneSingleton()); }); return singleton; } ``` ## 懒汉式 普通懒汉式单例(线程不安全) 使用互斥锁保证线程安全 (线程安全) ```cpp class LazyManSingleton { public: static std::shared_ptr getInstance(); // 禁用拷贝构造函数 LazyManSingleton(const LazyManSingleton&) = delete; // 禁用赋值操作符 LazyManSingleton& operator=(const LazyManSingleton&) = delete; ~LazyManSingleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } private: LazyManSingleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } static std::shared_ptr instance; static std::mutex mutex; }; std::shared_ptr LazyManSingleton::instance = nullptr; std::mutex LazyManSingleton::mutex; std::shared_ptr LazyManSingleton::getInstance() { // 使用互斥锁确保线程安全 std::lock_guard lock(mutex); if (!instance) { instance = std::shared_ptr(new LazyManSingleton()); } return instance; } ``` 静态局部变量 (线程安全) ```cpp class StaticLazyManSingleton { public: static StaticLazyManSingleton& getInstance() { static StaticLazyManSingleton instance; // 静态局部变量确保线程安全且延迟加载 return instance; } // 禁用拷贝构造函数 StaticLazyManSingleton(const StaticLazyManSingleton&) = delete; // 禁用赋值操作符 StaticLazyManSingleton& operator=(const StaticLazyManSingleton&) = delete; private: StaticLazyManSingleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } ~StaticLazyManSingleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } }; ``` 在C++11及更高版本中,使用静态局部变量来实现单例模式通常是线程安全的,这是因为C++11引入了线程安全的静态局部变量初始化机制。这种机制确保了只有一个线程能够初始化静态局部变量,避免了竞态条件 ## 特点 - 构造函数为私有类型,目的是禁止外部构造。 - 拷贝构造函数和赋值构造函数是私有类型,目的是禁止外部拷贝和赋值,确保实例的唯一性。 - 类中有一个获取实例的静态方法,可以全局访问。