C++ 单例模式

在看Apollo代码时发现一个写法奇怪的Singleton类,查了查发现很有来头

// @brief Thread-safe, no-manual destroy Singleton template
template <typename T>
class Singleton {
 public:
  // @brief Get the singleton instance
  static T* get() {
    pthread_once(&p_once_, &Singleton::new_);
    return instance_;
  }

 private:
  Singleton();
  ~Singleton();

  // @brief Construct the singleton instance
  static void new_() { instance_ = new T(); }

  // @brief  Destruct the singleton instance
  // @note Only work with gcc
  __attribute__((destructor)) static void delete_() {
    typedef char T_must_be_complete[sizeof(T) == 0 ? -1 : 1];
    (void)sizeof(T_must_be_complete);
    delete instance_;
  }

  static pthread_once_t p_once_;  // Initialization once control
  static T* instance_;            // The singleton instance
};

template <typename T>
pthread_once_t Singleton<T>::p_once_ = PTHREAD_ONCE_INIT;

template <typename T>
T* Singleton<T>::instance_ = nullptr;

单例模式

单例模式涉及一个单一的类,该类负责创建自己的对象,同时只允许一个对象存在。
对于一个全局使用的类可能会被频繁的创建和销毁时,使用单例模式来实现可以起到节省系统资源的目的。
比如配置文件的管理,就可以用单例实现。

实现思路

  • 该类的构造函数定义为私有,这样其他地方的代码无法通过调用该类的构造方法来实例化;
  • 类内提供一个静态方法,当调用该方法时,如果类的对象引用不为空则返回,如果为空则创建一个对象并引用。
    《C++面试中的singleton类》里解释了为什么用引用而不是指针:因为Singleton返回的实例的生存期是由类本身决定的,而不是用户代码。

    一个简单实现

class Singleton
{
  public:
     static Singleton& Instance()
     {
         static Singleton singleton;
         return singleton;
     }
  private:
       Singleton() { };
};

在访问函数Instance()中将实例singleton声明为局部静态变量,以保证其

  • 具有全局寿命
  • 局部可见
  • 只有第一次进入函数时才被初始化

这样做的一个好处是,实例只有在被调用时才创建(懒汉式),节约了系统资源
还有一种方式是,在构造函数中就创建实例(饿汉式),这种方式会提前抢占系统资源,但对多线程也是安全的(不会创建多个实例)

懒汉式多线程安全

当一个线程第一次调用Instance()时,会创建实例,假设在此时函数被另一个线程调用,又会生成一个实例。

class Singleton{
public:
    static Singleton& Instance(){
        if (singleton == NULL){
            Lock lock;
            if (singleton == NULL){
                singleton = new Singleton();
            }
            return *singleton;
        }
        return *singleton;
    }
private:
    static Singleton* volatile singleton;
};

如上,使用双重检查的方式以保证线程安全
volatile修饰符是告诉编译器该变量可能会被未知原因(系统、线程等)的修改,不要对该处代码做优化。

单例类的重用

很显然,Singleton类是无法重用的,但可用模板实现Instance()的重用以达到目的
具体实现可以参考本文开头引用的Apollo代码


### C++ 单例设计模式的实现与使用 #### 实现线程安全的懒汉式单例模式 为了确保实例仅被创建一次并提供全局访问点,在C++中可以通过静态成员函数来控制类对象的创建过程。下面展示了一种常见的懒加载方式,即当第一次调用 `getInstance` 方法时才初始化该类的对象[^1]。 ```cpp class Singleton { private: static std::unique_ptr<Singleton> instance; // 私有化构造函数防止外部new Singleton() {} public: ~Singleton() {} // 删除拷贝构造和赋值操作符以阻止复制行为 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; // 获取唯一实例的方法, 使用双重检测锁机制保证多线程环境下的安全性 static Singleton* getInstance() { if (instance.get() == nullptr) { // 第一层判断减少竞争开销 std::lock_guard<std::mutex> lock(mutex_); if (!instance) { // 双重检查锁定 instance.reset(new Singleton()); } } return instance.get(); } private: static std::once_flag initFlag; // 初始化标志位 static std::mutex mutex_; // 同步互斥量用于保护临界区 }; std::unique_ptr<Singleton> Singleton::instance(nullptr); std::once_flag Singleton::initFlag; std::mutex Singleton::mutex_; ``` 这种方法不仅实现了延迟加载(Lazy Initialization),还通过双层校验加锁的方式提高了并发性能,适用于高并发场景下资源的竞争管理[^2]。 #### 饿汉式单例模式 如果应用程序启动之初就需要立即获取到这个唯一的实例,则可以采用饿汉式的写法。这种方式非常简单直观,因为实例是在定义的时候就被创建出来的,所以不存在任何同步问题[^3]。 ```cpp class Singleton { private: // 构造器私有化 Singleton() {}; public: // 禁止拷贝 Singleton(Singleton const&) = delete; void operator=(Singleton const&) = delete; // 提供全局访问接口 static Singleton& GetInstance(){ static Singleton instanse; return instanse; } }; // 客户端代码可以直接这样获得单例对象: void someFunction(){ auto &singletonObj = Singleton::GetInstance(); } ``` 此版本利用了局部静态变量特性——它们会在首次执行所在语句之前完成初始化工作,并且整个程序生命周期内只做这一次初始化动作[^4]。 #### 应用场合说明 对于大多数情况而言,推荐优先考虑基于局部静态变量特性的饿汉式方案,除非确实存在明显的按需分配需求或者担心编译期就占用过多内存空间等问题才会转向更复杂的懒加载形式[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值