如何实现单例模式?

概念

单例模式(Singleton Pattern)是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要控制资源访问的场景,如数据库连接、日志记录或者配置管理等。

方式

懒汉式单例

懒汉式单例是在第一次被请求时创建实例。为了确保线程安全,通常使用 std::mutex 进行保护。

cpp 复制代码
#include <iostream>  
#include <mutex>  

class Singleton {  
public:  
    // 禁止拷贝构造和赋值  
    Singleton(const Singleton&) = delete;  
    Singleton& operator=(const Singleton&) = delete;  

    static Singleton& getInstance() {  
        static Singleton instance; // 此时实例在第一次调用时创建  
        return instance;  
    }  

    void someMethod() {  
        std::cout << "Doing something in Singleton." << std::endl;  
    }  

private:  
    Singleton() {  
        std::cout << "Singleton Constructor" << std::endl; // 建构函数  
    }  
    
    ~Singleton() {  
        std::cout << "Singleton Destructor" << std::endl; // 析构函数  
    }  
};  

int main() {  
    Singleton::getInstance().someMethod(); // 访问单例  
    return 0;  
}

代码解析

  • 私有构造函数:确保外部不能直接创建实例。
  • 禁止拷贝:复制构造函数和赋值运算符被删除,以防止复制。
  • 静态局部变量:在静态方法 getInstance 中声明一个静态局部变量,这个变量 在第一次使用时初始化,后续调用将返回相同的实例。
  • 线程安全:C++11 及以上版本中,局部静态变量在多线程中是安全的,不需要额外的锁。

饿汉式单例

饿汉式单例在程序启动时就创建实例,不论是否被使用。这样可以避免多线程中的同步问题,但在资源使用上可能会浪费。

cpp 复制代码
#include <iostream>  

class Singleton {  
public:  
    // 禁止拷贝构造和赋值  
    Singleton(const Singleton&) = delete;  
    Singleton& operator=(const Singleton&) = delete;  

    static Singleton& getInstance() {  
        return instance; // 直接返回实例  
    }  

    void someMethod() {  
        std::cout << "Doing something in Singleton." << std::endl;  
    }  

private:  
    Singleton() {  
        std::cout << "Singleton Constructor" << std::endl; // 建构函数  
    }  

    ~Singleton() {  
        std::cout << "Singleton Destructor" << std::endl; // 析构函数  
    }  

    static Singleton instance; // 静态实例  
};  

// 定义静态成员  
Singleton Singleton::instance;  

int main() {  
    Singleton::getInstance().someMethod(); // 访问单例  
    return 0;  
}

代码解析

  • 静态实例:在类的内部声明一个静态成员 instance,在类外定义该成员。
  • 构造函数:与懒汉式相同,构造函数和析构函数是私有的,以防止外部实例化。

线程安全的懒汉式单例(使用锁)

这种方法在创建单例实例时使用互斥锁来实现控制,确保多个线程不同时创建。

cpp 复制代码
#include <iostream>  
#include <mutex>  

class Singleton {  
public:  
    static Singleton* getInstance() {  
        std::lock_guard<std::mutex> lock(mu); // 加锁  
        if (!instance)  
            instance = new Singleton(); // 延迟加载  
        return instance;  
    }  

    void someMethod() {  
        std::cout << "Doing something in Singleton." << std::endl;  
    }  

private:  
    Singleton() = default; // 私有构造函数  
    ~Singleton() = default; // 私有析构函数  

    Singleton(const Singleton&) = delete; // 禁止拷贝构造  
    Singleton& operator=(const Singleton&) = delete; // 禁止赋值  

    static Singleton* instance; // 静态实例指针  
    static std::mutex mu; // 互斥量  
};  

// 定义静态成员  
Singleton* Singleton::instance = nullptr;  
std::mutex Singleton::mu;  

int main() {  
    Singleton::getInstance()->someMethod(); // 访问单例  
    return 0;  
}

代码解析

  • 互斥量:使用 std::mutex 控制访问,以实现线程安全。每当访问 getInstance 时,lock_guard 自动加锁。
  • 动态分配:实例在首次访问时通过 new 创建,依赖用户在适当的位置调用 delete 销毁对象。

采用 std::call_once

C++11 中引入的 std::call_once 可以确保线程安全的单例实现。

cpp 复制代码
#include <iostream>  
#include <mutex>  

class Singleton {  
public:  
    static Singleton& getInstance() {  
        std::call_once(initInstanceFlag, &Singleton::initSingleton);  
        return *instance;  
    }  

    void someMethod() {  
        std::cout << "Doing something in Singleton." << std::endl;  
    }  

private:  
    Singleton() {  
        std::cout << "Singleton Constructor" << std::endl;  
    }  
    
    ~Singleton() {  
        std::cout << "Singleton Destructor" << std::endl;  
    }  

    static void initSingleton() {  
        instance = new Singleton();  
    }  

    static Singleton* instance;  
    static std::once_flag initInstanceFlag; // 初始化标志  
};  

// 静态成员初始化  
Singleton* Singleton::instance = nullptr;  
std::once_flag Singleton::initInstanceFlag;  

int main() {  
    Singleton::getInstance().someMethod();  
    return 0;  
}

代码解析

  • std::once_flag:用于保证指定的函数只被调用一次。
  • std::call_once:确保在多线程环境下只会初始化一次实例。

总结

单例模式可以通过多种方式实现,包括懒汉式、饿汉式、线程安全的懒汉式、使用 std::call_once 等。选择合适的实现方式主要取决于项目的需求、线程安全需求以及如何管理生命周期等因素。对于大多数情况,使用 C++11 提供的懒汉式结合 std::call_once 是最推荐的做法,因为它既保证了线程安全,又简洁易懂。

相关推荐
晨星052711 小时前
软件设计模式之单例模式
单例模式·设计模式
code bean2 天前
【wpf】WPF开发避坑指南:单例模式中依赖注入导致XAML设计器崩溃的解决方案
单例模式·wpf
是三好4 天前
单例模式(Singleton Pattern)
java·开发语言·算法·单例模式
青春易逝丶4 天前
单例模式
单例模式
YA3334 天前
java设计模式一、单例模式
java·单例模式·设计模式
枫景Maple4 天前
Unity中多线程与高并发下的单例模式
unity·单例模式·游戏引擎
iiiiaaiashah5 天前
单例模式的mock类注入单元测试与友元类解决方案
java·开发语言·单例模式
jingfeng5149 天前
线程池及线程池单例模式
linux·开发语言·单例模式
Brookty9 天前
深入解析Java并发编程与单例模式
java·开发语言·学习·单例模式·java-ee
Meteors.10 天前
23种设计模式——单例模式(Singleton)详解
单例模式·设计模式