单例设计模式

单例模式是一种常用的软件设计模式,其目标是确保一个类只有一个实例,并提供一个全局访问点。在C++中,可以通过以下步骤实现单例模式:

  1. 私有化构造函数,以防止外界创建单例类的对象。
  2. 使用类的私有静态指针变量指向类的唯一实例。
  3. 使用一个公有的静态方法获取该实例。

以下是一个简单的C++单例模式的实现示例:

class Singleton
{
private:
    static Singleton* instance;
    Singleton() {}
    ~Singleton() {}
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);

public:
    static Singleton* getInstance()
    {
        if (instance == NULL)
            instance = new Singleton();
        return instance;
    }
};

// Initialize static member
Singleton* Singleton::instance = NULL;

这个例子中,Singleton类的构造函数是私有的,这意味着不能从类的外部创建Singleton对象。唯一的Singleton实例是通过getInstance()函数创建的,该函数检查instance是否为NULL,如果是,则创建一个新的Singleton对象1

然而,这个简单的实现在多线程环境下可能会有问题。如果两个线程同时调用getInstance(),可能会创建两个Singleton实例。为了解决这个问题,可以使用双检查锁定模式(Double-Checked Locking Pattern)1

此外,C++11引入了内存模型,提供了原子操作来实现内存的同步访问,因此在C++11后可以正确地跨平台实现双检查锁定模式1

请注意,这只是单例模式的一种实现方式,还有其他的实现方式,例如懒汉式、饿汉式等2。具体选择哪种实现方式取决于你的具体需求和环境。在选择合适的实现方式时,你需要考虑多线程安全、资源管理、程序的性能和复杂性等因素2

在C++中,双重检查锁定模式(Double-Checked Locking Pattern,DCLP)也被用于实现单例模式的“惰性初始化”。这种模式在某些语言在某些硬件平台的实现可能是不安全的。有的时候,这一模式被看做是反模式1

以下是一个C++中使用双重检查锁定模式的例子1

std::mutex mlock;
class Singleton {
private:
    Singleton() {}
    static Singleton * ptr;
public:
    static Singleton * Create() {
        if (ptr == nullptr) { // 第一次检查
            std::lock_guard<std::mutex> lock(mlock); // 获取锁
            if (ptr == nullptr) { // 第二次检查
                ptr = new Singleton();
            }
        }
        return ptr;
    }
};
Singleton * Singleton::ptr = nullptr;

在这个例子中,Singleton::Create()方法首先检查ptr是否为nullptr(第一次检查)。如果ptrnullptr,则获取锁并再次检查ptr是否为nullptr(第二次检查)。只有在ptrnullptr的情况下,才会创建新的Singleton实例。这样,除了初始化的时候会加锁,后续的调用都是直接返回,解决了多余的性能消耗2

需要注意的是,这种模式在多线程环境中可能会有问题,因为C++的内存模型允许“无序写入”,有些编译器因为性能原因,可能会把上述步骤中的 2 和 3 进行重排序2。为了解决这个问题,可以使用volatile关键字,但是使用volatile关键字的代码也不能保证正常工作在多线程环境中1。具体原因分析请参考"C++ and the Perils of Double-Checked Locking"这篇论文1

 
 
posted @ 2023-12-08 21:56  ponder776  阅读(1)  评论(0编辑  收藏  举报