创建型模式-单例模式

创建型模式-单例模式

002456-1721492696c292


1. 意图

意图是模式的简短、精炼的一句话总结,回答“这个模式是什么”的问题。

单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点

2. 动机

动机是解释为什么需要这个模式,描述模式要解决的具体问题和场景。

对于一些类中,只有一个实例是很重要的。例如一个系统中,只应该有一个文件系统和窗口管理器,

3. 解决方法

所有单例的实现都包含以下两个相同的步骤:

  • 将默认构造函数设为私有或者保护, 防止其他对象使用单例类的 new运算符。
  • 新建一个静态构建方法作为构造函数。 该函数会 “偷偷” 调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。

如果你的代码能够访问单例类, 那它就能调用单例类的静态方法。 无论何时调用该方法, 它总是会返回相同的对象。

4. 单例模式结构

image-20260103103624534

5. 实现

5.1 懒汉式-单线程-手动释放版

"懒"的体现:“节能体现”,只在第一次调用 getInstance() 时才创建实例,不到万不得已(真正需要)绝不创建对象。

class USingleton 
{
public:
    // 全局访问点
    static USingleton* getInstance()
    {
        if (instance == nullptr)
        {
            instance = new USingleton();
        }
        return instance;
    }

    void doSomething()
    {
        std::cout << "Doing something..." << std::endl;
    }

    // 清理资源(可选)
    static void destroy()
    {
        delete instance;
        instance = nullptr;
    }

private:
    static USingleton* instance;

    // 私有构造函数
    USingleton() = default;

    // 私有拷贝构造函数和赋值运算符
    USingleton(const USingleton&) = delete;
    USingleton& operator=(const USingleton&) = delete;
};

/// xxx.cpp
// 静态成员初始化
USingleton* USingleton::instance = nullptr;

特性

  1. 懒加载:首次调用getInstance()时才创建实例
  2. 简单直观:最基础的单例实现
  3. 内存泄漏风险:需要手动调用destroy()清理
优点 缺点
实现简单:代码简洁,易于理解 非线程安全:多线程下可能创建多个实例
延迟初始化:节省启动时资源占用 内存泄漏风险:C++中需手动管理内存
代码直观:适合教学单例模式原理 违反RAII:不符合C++资源管理最佳实践
灵活控制:可控制初始化时机 异常不安全:构造函数异常可能导致状态不一致
效率问题:线程安全版本需加锁影响性能

5.2 懒汉式-单线程-自动释放版

借助类成员变量(内部类,完成内部类自动析构调用)。

class USingleton
{
public:
    // 全局访问点
    static USingleton* getInstance()
    {
        if (instance == nullptr)
        {
            instance = new USingleton();
            std::cout << "construct ..." << std::endl;
        }
        return instance;
    }

    void doSomething()
    {
        std::cout << "Doing something..." << std::endl;
    }

private:
    // 内部类,用于垃圾回收
    class GC
    {
    public:
        ~GC()
        {
            if (instance != NULL)
            {
                delete instance;
                instance = nullptr;
                std::cout << "destroy ..." << std::endl;
            }
        }
    };

    static GC gc;

private:
    static USingleton* instance;

    // 私有构造函数
    USingleton() = default;

    // 私有拷贝构造函数和赋值运算符
    USingleton(const USingleton&) = delete;
    USingleton& operator=(const USingleton&) = delete;
};

/// xxx.cpp
// 在程序启动前就创建实例
USingleton* USingleton::instance = nullptr;
//全局静态变量,会被自动销毁,从而实现对单例的垃圾回收
USingleton::GC USingleton::gc;

5.3 懒汉式-多线程版

双重检查锁定版本-关键实现代码:

class USingleton 
{
public:
    // 全局访问点
    static USingleton* getInstance()
    {
        // 第一次检查(无锁)
        if (instance == nullptr) {
            // 获取锁
            std::lock_guard<std::mutex> lock(mutex);

            // 第二次检查(持有锁)
            if (instance == nullptr)
            {
                instance = new USingleton();
                std::cout << "construct ..." << std::endl;
            }
        }
        return instance;
    }

    void doSomething()
    {
        std::cout << "Doing something..." << std::endl;
    }

    // 清理资源(可选)
    static void destroy()
    {
        std::lock_guard<std::mutex> lock(mutex);
        if (instance != nullptr)
        {
            delete instance;
            instance = nullptr;
            std::cout << "~destory ..." << std::endl;
        }
    }

private:
    static USingleton* instance;
    static std::mutex mutex;

    // 私有构造函数
    USingleton() = default;

    // 私有拷贝构造函数和赋值运算符
    USingleton(const USingleton&) = delete;
    USingleton& operator=(const USingleton&) = delete;
};

/// xxx.cpp
// 静态成员初始化
USingleton* USingleton::instance = nullptr;
std::mutex USingleton::mutex;

5.4 懒汉式-Meyer's Singleton(推荐)

这是一种基于局部静态变量的懒汉式单例实现,由C++专家Scott Meyers在《Effective C++》中提出,被认为是现代C++中最优雅的单例实现

在单例模式中,坚持使用返回引用(Meyer's方式),除非有特殊需求(如需要兼容C接口)。不要因为"可以转换"就随意混用(返回指针形式),保持代码的清晰性和一致性更重要。

class USingleton 
{
public:
    // 全局访问点
    static USingleton& getInstance()
    {
        static USingleton s_us_inst;
        return s_us_inst;
    }

    void doSomething()
    {
        std::cout << "Doing something..." << std::endl;
    }

private:
    // 私有构造函数
    USingleton() = default;

    // 私有拷贝构造函数和赋值运算符
    USingleton(const USingleton&) = delete;
    USingleton& operator=(const USingleton&) = delete;
};

5.5 饿汉式

饿汉式最核心的体现就是 "时间确定性"。它在程序生命周期的最早期(main函数执行之前)就完成初始化,这种设计哲学认为:

  • "已知的坏消息优于未知的惊喜":宁愿在启动时就知道失败,也不要在业务高峰时突然崩溃
  • "确定的延迟优于不确定的等待":把初始化延迟明确放在启动阶段,而不是隐藏在业务调用中
  • "可控的启动过程优于随机的运行时行为":系统管理员可以明确知道"程序要么完全启动成功,要么完全失败"
class USingleton 
{
public:
    // 全局访问点
    static USingleton& getInstance()
    {
        return s_us_inst;
    }

    void doSomething()
    {
        std::cout << "Doing something..." << std::endl;
    }

private:
    // 私有构造函数
    USingleton() = default;
    static USingleton s_us_inst;

    // 私有拷贝构造函数和赋值运算符
    USingleton(const USingleton&) = delete;
    USingleton& operator=(const USingleton&) = delete;
};

/// xxx.cpp 在程序启动前就创建实例
USingleton USingleton::s_us_inst;

5.6 宏实现版本

针对单例的实现,如果程序中存在多个,那么其实现大致相同,导致重复代码的出现。可以使用模板或者宏的方式来解决重复度。

// singleton_macro_simple.h
#ifndef SINGLETON_MACRO_SIMPLE_H
#define SINGLETON_MACRO_SIMPLE_H

#include <mutex>

// 声明和实现在一起(适合头文件只有的情况)
#define SINGLETON(classname) \
private: \
    static classname* instance; \
    static std::mutex mtx; \
    classname() {} \
    classname(const classname&) = delete; \
    classname& operator=(const classname&) = delete; \
public: \
    static classname* getInstance() { \
        if (instance == nullptr) { \
            std::lock_guard<std::mutex> lock(mtx); \
            if (instance == nullptr) { \
                instance = new classname(); \
            } \
        } \
        return instance; \
    } \
    static void destroy() { \
        std::lock_guard<std::mutex> lock(mtx); \
        if (instance != nullptr) { \
            delete instance; \
            instance = nullptr; \
        } \
    }

// 在cpp文件中定义静态成员
#define SINGLETON_INSTANCE(classname) \
    classname* classname::instance = nullptr; \
    std::mutex classname::mtx;

#endif

/// 测试类
class USingleton
{
    SINGLETON(USingleton)
public:
    void doSomething()
    {
        std::cout << "Doing something..." << std::endl;
    }
};

/// xxx.cpp
#include "TestSingleton.h"
SINGLETON_INSTANCE(USingleton)

5.7 模板版本

// 更好的方案:使用模板而不是宏
template<typename T>
class Singleton {
protected:
    Singleton() = default;

public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static T& getInstance() {
        static std::once_flag initFlag;
        static T* instance = nullptr;

        std::call_once(initFlag, []() {
            instance = new T();
        });

        return *instance;
    }
};

/// 测试类
class USingleton : public Singleton<USingleton>
{
public:
    void doSomething()
    {
        std::cout << "Doing something..." << std::endl;
    }
};

5.8 宏+模板(推荐)

// 首先需要定义Singleton模板类
template<typename T>
class Singleton {
public:
    static T& getInstance()
    {
        static T s_instance;
        return s_instance;
    }

protected:
    Singleton() = default;
    virtual ~Singleton() = default;

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


// 宏定义
#define SINGLETON_DECLARE(ClassName)                              \
private:                                                          \
    ClassName();                                                  \
    ClassName(const ClassName&) = delete;                         \
    ClassName& operator=(const ClassName&) = delete;              \
    friend class Singleton<ClassName>;                            \
public:                                                           \
    static ClassName& getInstance() {                             \
        return Singleton<ClassName>::getInstance();               \
    }                                                             \
    static ClassName* pointer() {                                 \
        return &getInstance();                                    \
    }


/// 测试类
class USingleton
{
    SINGLETON_DECLARE(USingleton)
public:
    void doSomething()
    {
        std::cout << "Doing something..." << std::endl;
    }

};

/// xxx.cpp
#include "TestSingleton.h"

USingleton::USingleton()
{
    std::cout << "Hello, World" << std::endl;
}

注意,如果在宏定义中, ClassName()声明了没有实现,则需要在对应的cpp中实现构造函数。

6. 使用场景和优缺点

  • 如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。
  • 如果你需要更加严格地控制全局变量, 可以使用单例模式。

优缺点对比总结

方面 优势 劣势
实例控制 ✅ 严格保证唯一性 ❌ 灵活性受限
访问便利性 ✅ 全局直接访问 ❌ 可能造成过度使用
资源管理 ✅ 延迟初始化节省资源 ❌ 多线程下资源竞争
设计原则 - ❌ 违反单一职责原则
代码质量 - ❌ 可能掩盖设计缺陷
可测试性 - ❌ 单元测试困难

7. 参考

书籍:《设计模式:可复用面向对象软件的基础》;

单例的模版+宏的实现 | 公孙二狗
AI: DeepSeek

posted on 2026-01-03 13:32  Hakuon  阅读(67)  评论(0)    收藏  举报