如何实现单例模式?

概念

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

相关推荐
只做开心事18 小时前
Linux之线程池与单例模式
linux·单例模式
小乖兽技术1 天前
ASP.NET Core 中服务生命周期详解:Scoped、Transient 和 Singleton 的业务场景分析
后端·单例模式·asp.net
目目沐沐2 天前
单例模式
单例模式
HL_LOVE_C3 天前
设计模式-单例模式
单例模式·设计模式
赤水无泪3 天前
创建型模式-单例模式
单例模式
吾当每日三饮五升4 天前
C++单例模式跨DLL调用问题梳理
开发语言·c++·单例模式
臣妾写不来啊4 天前
创建型模式5.单例模式
单例模式
青岚岁叶5 天前
设计模式——泛型单例类
单例模式·设计模式·unity3d
晨辰星665 天前
java学习 单例模式
java·开发语言·单例模式
zhulangfly5 天前
【Java设计模式-1】单例模式,Java世界的“独苗”
java·单例模式·设计模式