返回

C++单例:打造真正的静态单例模式

后端

C++语言中的单例模式是一种非常重要的设计模式,它可以确保一个类只能被实例化一次。在之前的文章中,我们讨论了为什么不能直接将单例类设计为静态变量,而是需要实例化一个对象。现在,让我们进一步探讨一下这个问题,并提供一种改进的单例模式实现。

单例模式的局限性

传统的单例模式实现通常使用静态变量来保存单例对象的实例。这种实现方式虽然简单,但是在某些情况下存在局限性。例如,当存在子类继承单例类时,就会产生问题。

考虑以下示例代码:

class Base {
public:
    static Base& getInstance() {
        static Base instance;
        return instance;
    }
};

class Derived : public Base {
public:
    static Derived& getInstance() {
        static Derived instance;
        return instance;
    }
};

在这个例子中,我们定义了一个基类Base和一个子类Derived,这两个类都实现了单例模式。如果我们使用传统的实现方式,那么当我们调用Derived::getInstance()函数时,将会返回基类Base的实例,而不是子类Derived的实例。这是因为静态变量instance是在编译时就分配内存的,而子类Derived是在运行时才创建的。

为了解决这个问题,我们可以使用一种改进的单例模式实现,即双重校验锁机制。

双重校验锁机制

双重校验锁机制是一种线程安全的单例模式实现方式。它的基本原理是,在第一次访问单例对象时,先检查它是否已经存在,如果没有,再创建一个新的单例对象。

考虑以下示例代码:

class Base {
private:
    static Base* instance;
    static std::mutex mutex;

public:
    static Base& getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mutex);
            if (instance == nullptr) {
                instance = new Base();
            }
        }

        return *instance;
    }

protected:
    Base() {}
    virtual ~Base() {}
};

class Derived : public Base {
public:
    static Derived& getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mutex);
            if (instance == nullptr) {
                instance = new Derived();
            }
        }

        return *instance;
    }

protected:
    Derived() {}
    virtual ~Derived() {}
};

在这个例子中,我们使用了一个静态指针instance来保存单例对象的实例。当第一次访问单例对象时,我们首先检查instance是否为空,如果为空,再创建一个新的单例对象。为了保证线程安全,我们在创建单例对象时使用了std::mutex锁。

双重校验锁机制可以确保单例类的线程安全性和效率。它在第一次访问单例对象时进行同步,但在 subsequent 访问时,它可以避免不必要的同步开销。

总结

在本文中,我们讨论了C++单例模式的局限性,并提供了一种改进的单例模式实现,即双重校验锁机制。双重校验锁机制可以确保单例类的线程安全性和效率。