系列文章目录

【C++设计模式】总纲



单例模式(Singleton)

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
说人话就是,随便哪个源文件包含了单例类的头文件,他都能在任意位置访问到同一个实例。

1.实现要点

  • 私有化构造函数:禁止外部通过构造函数创建实例。
  • 提供一个静态方法:用于获取唯一实例,返回类的唯一实例。

2.优缺点

优点:

  • 控制实例数量:确保一个类只有一个实例,节省系统资源。
  • 全局访问:提供一个全局访问点,方便访问实例。
  • 延迟初始化:可以实现延迟初始化,优化系统性能。

缺点:

  • 全局状态:单例模式引入全局状态,可能导致代码难以理解和维护,但问题不大。
  • 并发问题:在多线程环境下实现单例模式需要考虑线程安全问题,增加了实现的复杂性。
  • 扩展性差:单例模式限制了类的扩展性,难以进行子类化。

3.实现方式

3.1基础单例模式

最基础的单例模式

优点:实现简单,适用于单线程。
缺点:可能会因为多个线程同时运行到创建实例,导致同时创建多个实例,线程不安全。

// Singleton.h
class Singleton {
public:
    static Singleton* getInstance();
    void add();

    // 禁止拷贝构造和赋值操作, 放在public里是为了编译时如果报错展示的信息更加详细
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
private:
    // 私有构造函数
    Singleton() {} 
    ~Singleton() {} 
    
    static Singleton* m_instance;
    int m_value = 0;
};

// Singleton.cpp
#include <iostream>
#include "Singleton.h"
Singleton* Singleton::m_instance = nullptr;  // 静态成员变量初始化

static Singleton::Singleton* getInstance() {
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}
void Singleton::add() {
    std::cout << ++m_value << std::endl;
}

// any.cpp
#inlcude "Singleton.h"
void doubleAdd(){
    Singleton::getInstance()->add();
    Singleton::getInstance()->add();
}

// main.cpp
#inlcude "Singleton.h"
int main() {
    Singleton* ins = Singleton::getInstance();
    ins->add();
    doubleAdd();
}

运行结果为:

1
2
3

3.2双重检查锁单例模式

很明显基础单例模式并不满足线程安全性,为了使单例模式的实现线程安全,引入双重检查锁。
在普通单例的基础上增加一个锁变量,并在获取单例时增加双重检查。

优点:线程安全,不需要每次调用都消耗锁资源,性能高
缺点:无

// Singleton.h
#include <mutex>
class Singleton {
public:
    static Singleton* getInstance();
    void add();

    // 禁止拷贝构造和赋值操作, 放在public里是为了编译时如果报错展示的信息更加详细
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
private:
    // 私有构造函数
    Singleton() {} 
    ~Singleton() {} 
    
    static Singleton* m_instance;
    static std::mutex m_mtx;
    int m_value = 0;
};

// Singleton.cpp
#include <iostream>
#include "Singleton.h"
Singleton* Singleton::m_instance = nullptr;  // 静态成员变量初始化
std::mutex Singleton::m_mtx;

static Singleton::Singleton* getInstance() {
    if (m_instance == nullptr) {
        std::lock_guard<std::mutex> lock(m_mtx);
        if (m_instance == nullptr) {  // 防止上锁前已经被其他线程实例化, 再次检查单例
            m_instance = new Singleton();
        }
    }
    return m_instance;
}
void Singleton::add() {
    std::cout << ++m_value << std::endl;
}

// any.cpp
#inlcude "Singleton.h"
void doubleAdd(){
    Singleton::getInstance()->add();
    Singleton::getInstance()->add();
}

// main.cpp
#inlcude "Singleton.h"
int main() {
    Singleton* ins = Singleton::getInstance();
    ins->add();
    doubleAdd();
}

3.3静态局部单例模式

单例模式的实现线程安全化的另一种方法,是使用静态局部变量。
相较于以上两种方法,无需在类内额外设置静态单例变量和锁变量。

优点:线程安全(C++11标准保证),无需静态成员变量初始化
缺点:无法传递参数给单例实例

// Singleton.h
#include <mutex>
class Singleton {
public:
    static Singleton* getInstance();
    void add();

    // 禁止拷贝构造和赋值操作, 放在public里是为了编译时如果报错展示的信息更加详细
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
private:
    // 私有构造函数
    Singleton() {} 
    ~Singleton() {} 

    int m_value = 0;
};

// Singleton.cpp
#include <iostream>
#include "Singleton.h"

static Singleton::Singleton* getInstance() {
    static Singleton instance;  // C++11标准内部保证了静态局部变量的线程安全性
    return m_instance;
}
void Singleton::add() {
    std::cout << ++m_value << std::endl;
}

// any.cpp
#inlcude "Singleton.h"
void doubleAdd(){
    Singleton::getInstance()->add();
    Singleton::getInstance()->add();
}

// main.cpp
#inlcude "Singleton.h"
int main() {
    Singleton* ins = Singleton::getInstance();
    ins->add();
    doubleAdd();
}

更多推荐