如何实现单例模式?

概念

单例模式(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 是最推荐的做法,因为它既保证了线程安全,又简洁易懂。

相关推荐
熬夜学编程的小王1 天前
【Linux篇】高并发编程终极指南:线程池优化、单例模式陷阱与死锁避坑实战
linux·单例模式·线程池·线程安全
Li小李同学Li2 天前
设计模式【cpp实现版本】
单例模式·设计模式
每次的天空4 天前
Android单例模式知识总结
android·单例模式
惊鸿醉4 天前
Unity_JK框架【4】MonoSystem 和 协程工具类 的剖析与实践
游戏·unity·单例模式
吃面必吃蒜5 天前
前端实战中的单例模式:以医疗药敏管理为例
前端·javascript·单例模式·设计模式
编码小笨猪7 天前
[ 设计模式 ] | 单例模式
c++·单例模式·设计模式
Moso_Rx8 天前
javaEE——单例模式
java·单例模式·java-ee
工藤新一¹9 天前
C++/SDL 进阶游戏开发 —— 双人塔防(代号:村庄保卫战 20)
c++·单例模式·游戏引擎·sdl·c++游戏开发
wjm0410069 天前
C++八股--three day --设计模式之单例和工厂
c++·单例模式·设计模式
Cuit小唐9 天前
C++ 单例模式详解
开发语言·c++·单例模式