自学内容网 自学内容网

C++—单例设计模式

单例设计模式

C++中的单例设计模式是一种常用的软件设计模式,其核心目的是确保一个类仅有一个实例,并提供一个全局访问点来获取这个实例。以下是对C++单例设计模式的详细解释:

一、单例设计模式的定义

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。

二、单例设计模式的实现方式

在C++中,单例模式的实现方式多种多样,但基本思想都是将构造函数私有化,并通过一个静态方法提供类的唯一实例。以下是几种常见的实现方式:

  1. 懒汉式(线程不安全)
    • 延迟加载实例,即在需要时才创建实例。
    • 但在多线程环境下存在线程安全问题。
  2. 懒汉式(线程安全)
    • 通过加锁(如使用std::mutex)来保证在多线程环境下的线程安全。
    • 但每次访问实例时都需要加锁,可能会影响性能。
  3. 饿汉式
    • 在程序启动时立即创建实例,因此本身是线程安全的。
    • 但无论是否使用实例,都会立即创建,可能会浪费资源。
  4. 双重检查锁定(Double-Check Locking)
    • 一种优化懒汉式线程安全实现的方法。
    • 通过两次检查实例是否存在来减少加锁的次数,从而提高性能。
  5. 静态局部变量(C++11推荐)
    • 利用C++11中静态局部变量的线程安全特性来实现单例。
    • 这种方法既实现了延迟加载,又保证了线程安全。
  6. 使用智能指针
    • 将实例封装在智能指针中,如std::unique_ptrstd::shared_ptr,以自动管理内存。
  7. 使用std::call_once
    • C++11引入的std::call_once函数可以保证某个函数在程序执行期间只被调用一次。
    • 可以利用这个特性来实现线程安全的单例初始化。

三、单例设计模式的优缺点

优点

  1. 控制资源访问:确保对共享资源的独占访问。
  2. 简化全局访问:提供一个全局访问点,方便使用。
  3. 实现数据共享:可以在多个对象之间共享数据。

缺点

  1. 单例对象生命周期管理困难:单例对象通常在整个应用程序的生命周期内都存在,这可能导致资源无法及时释放。
  2. 扩展性差:如果需要创建多个实例,则需要对代码进行较大修改。
  3. 隐藏类的依赖关系:使用单例模式的类通常与其他类紧密耦合,这可能导致代码难以理解和维护。

四、单例设计模式的应用场景

单例设计模式适用于以下场景:

  1. 全局配置管理:如读取配置文件并管理配置信息。
  2. 数据库连接池:管理数据库连接,确保全局只有一个连接池。
  3. 日志记录器:确保全局只有一个日志记录器实例,用于记录应用程序的日志信息。
  4. 状态管理:在需要全局状态管理的场景中,如游戏开发中的游戏状态管理等。

总之,C++中的单例设计模式是一种非常有用的设计模式,但在使用时需要注意其优缺点和适用场景,以避免过度使用或误用导致的问题。

五、案例测试

1.懒汉式(线程安全)

#include <iostream>  
#include <mutex>  
  
class Singleton {  
private:  
    static Singleton* instance;  
    static std::mutex mtx;  
  
    Singleton() {} // 私有构造函数,防止外部直接创建实例  
  
    // 禁止拷贝构造函数和赋值运算符  
    Singleton(const Singleton&) = delete;  
    Singleton& operator=(const Singleton&) = delete;  
  
public:  
    static Singleton* getInstance() {  
        std::lock_guard<std::mutex> lock(mtx); // 使用锁保护  
        if (!instance) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
  
    void doSomething() {  
        std::cout << "Doing something..." << std::endl;  
    }  
  
    // 静态成员需要在类外初始化  
    static Singleton* instance_init() {  
        return nullptr;  
    }  
    static std::mutex mtx_init() {  
        return std::mutex();  
    }  
};  
  
// 初始化静态成员  
Singleton* Singleton::instance = Singleton::instance_init();  
std::mutex Singleton::mtx = Singleton::mtx_init();  
  
int main() {  
    Singleton* s1 = Singleton::getInstance();  
    Singleton* s2 = Singleton::getInstance();  
  
    if (s1 == s2) {  
        std::cout << "s1 and s2 are the same instance." << std::endl;  
    }  
  
    s1->doSomething();  
    s2->doSomething();  
  
    return 0;  
}

注意:虽然上面的代码使用了std::mutex来保证线程安全,但在实际使用中,如果单例的创建和销毁不是性能瓶颈,并且你确信单例的创建是线程安全的(例如,在程序启动时由主线程创建),那么可能不需要额外的线程安全措施。

2.静态局部变量(C++11推荐)

#include <iostream>  
  
class Singleton {  
private:  
    Singleton() {} // 私有构造函数,防止外部直接创建实例  
  
    // 禁止拷贝构造函数和赋值运算符  
    Singleton(const Singleton&) = delete;  
    Singleton& operator=(const Singleton&) = delete;  
  
    static Singleton& getInstanceHelper() {  
        static Singleton instance; // 局部静态变量,线程安全  
        return instance;  
    }  
  
public:  
    static Singleton& getInstance() {  
        return getInstanceHelper();  
    }  
  
    void doSomething() {  
        std::cout << "Doing something..." << std::endl;  
    }  
};  
  
int main() {  
    Singleton& s1 = Singleton::getInstance();  
    Singleton& s2 = Singleton::getInstance();  
  
    // s1 和 s2 实际上是同一个实例的引用  
    std::cout << "&s1: " << &s1 << std::endl;  
    std::cout << "&s2: " << &s2 << std::endl;  
  
    s1.doSomething();  
    s2.doSomething();  
  
    return 0;  
}

在这个例子中,我使用了C++11的局部静态变量特性来确保单例的线程安全性和懒加载。这种方法既简单又高效,是推荐的实现方式之一。注意,由于我们返回的是引用而不是指针,因此不需要担心内存管理问题。


原文地址:https://blog.csdn.net/weixin_45706195/article/details/142560932

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!