0

0

c++如何实现单例模式_c++ 饿汉式与懒汉式线程安全实现【方法】

冰火之心

冰火之心

发布时间:2026-01-03 11:56:41

|

597人浏览过

|

来源于php中文网

原创

饿汉式单例启动即初始化,天然线程安全;懒汉式首次调用才创建,需用std::call_once或双重检查锁定保障线程安全,但易出错,推荐优先使用饿汉式。

c++如何实现单例模式_c++ 饿汉式与懒汉式线程安全实现【方法】

饿汉式单例:启动即初始化,天然线程安全

饿汉式在程序加载时就完成实例构造,后续所有调用都直接返回已创建的对象指针,不存在多线程竞争问题,无需加锁。

关键点在于 static 成员变量的初始化时机由编译器保证——C++11 起,static 局部变量的初始化是线程安全的;而静态成员变量(如类内定义的 static Instance*)在 main() 执行前完成,且仅一次。

常见错误是把指针声明和 new 拆开写,导致非原子操作:

class Singleton {
private:
    static Singleton* instance;
    Singleton() = default;  // 防止外部构造
public:
    static Singleton* getInstance() {
        return instance;  // ❌ instance 可能为 nullptr 或未初始化
    }
};
Singleton* Singleton::instance = new Singleton();  // ✅ 此行才真正构造

更推荐写法(C++11+):

立即学习C++免费学习笔记(深入)”;

class Singleton {
private:
    Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
public:
    static Singleton& getInstance() {
        static Singleton instance;  // ✅ 局部静态变量,线程安全初始化
        return instance;
    }
};
  • 必须禁用拷贝构造与赋值,否则可能意外复制出多个对象
  • 返回引用比返回指针更安全,避免用户误删或置空
  • 析构顺序不可控:局部静态对象在 main 结束后按逆序销毁,若其他静态对象依赖它,可能访问已析构对象

懒汉式单例:首次调用才创建,需手动保障线程安全

懒汉式延迟资源占用,但 getInstance() 中的判空 + 构造逻辑不是原子操作,多线程下极易出现重复 new 或返回未完全构造的对象。

典型错误写法(双重检查锁定漏锁):

Spell.tools
Spell.tools

高颜值AI内容营销创作工具

下载
static Singleton* getInstance() {
    if (instance == nullptr) {           // 第一次检查
        instance = new Singleton();      // ❌ 构造+赋值非原子,可能被重排,其他线程看到半初始化对象
    }
    return instance;
}

正确实现(C++11 double-checked locking pattern):

class Singleton {
private:
    static std::atomic instance;
    static std::mutex mtx;
    Singleton() = default;
public:
    static Singleton* getInstance() {
        Singleton* tmp = instance.load(std::memory_order_acquire);
        if (tmp == nullptr) {
            std::lock_guard lock(mtx);
            tmp = instance.load(std::memory_order_relaxed);
            if (tmp == nullptr) {
                tmp = new Singleton();
                instance.store(tmp, std::memory_order_release);
            }
        }
        return tmp;
    }
};
std::atomic Singleton::instance{nullptr};
std::mutex Singleton::mtx;
  • 必须用 std::atomic 替代裸指针,否则无法防止指令重排
  • memory_order_acquirememory_order_release 保证构造完成后再对其他线程可见
  • 两次判空缺一不可:第一次避免无谓加锁,第二次防止加锁后已被其他线程创建
  • 不建议手写 DCLP —— 容易出错,优先用局部静态变量(饿汉式)或 std::call_once

更现代的懒汉式替代:std::call_once + once_flag

相比手写 DCLP,std::call_once 更简洁、不易出错,且由标准库保证绝对只执行一次。

class Singleton {
private:
    static Singleton* instance;
    static std::once_flag init_flag;
    Singleton() = default;
public:
    static Singleton* getInstance() {
        std::call_once(init_flag, []() {
            instance = new Singleton();
        });
        return instance;
    }
};
Singleton* Singleton::instance = nullptr;
std::once_flag Singleton::init_flag;
  • std::call_once 内部已做线程同步,无需额外锁或原子操作
  • 适合初始化逻辑较重、且确实需要延迟加载的场景
  • 注意:instance 仍需声明为 static,且不能在 lambda 外提前使用
  • 析构仍需手动管理(比如用 std::unique_ptr 包裹并注册 atexit),否则内存泄漏

饿汉式 vs 懒汉式:选型关键看初始化成本与依赖关系

饿汉式看似“浪费”,实则规避了绝大多数线程安全陷阱;懒汉式看似灵活,却把复杂性推给了开发者。

真实项目中容易被忽略的点:

  • 如果单例构造函数中调用了其他尚未初始化的全局对象(比如另一个单例),饿汉式可能因静态初始化顺序未定义而崩溃
  • 懒汉式若用 std::call_once,其内部实现依赖 OS 级同步原语,在极低概率下(如 fork 后)可能异常,但绝大多数场景可忽略
  • C++20 引入 constinit,但目前对单例帮助有限,仍无法解决跨编译单元初始化顺序问题
  • 真正需要懒汉式的场景极少——多数所谓“耗资源”其实是错觉,真正瓶颈往往在 I/O 或网络,而非内存分配

除非明确知道构造开销极大、且确定不会引发静态初始化依赖,否则默认用饿汉式(局部静态变量版本)最省心。

相关专题

更多
c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

52

2025.08.29

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

99

2025.10.23

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

204

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

190

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

47

2026.01.05

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

480

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

143

2025.12.24

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

3

2026.01.16

java数据库连接教程大全
java数据库连接教程大全

本专题整合了java数据库连接相关教程,阅读专题下面的文章了解更多详细内容。

26

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号