0

0

c++中如何使用std::call_once实现单例模式初始化_c++线程安全【实例】

裘德小鎮的故事

裘德小鎮的故事

发布时间:2026-01-25 13:37:57

|

902人浏览过

|

来源于php中文网

原创

std::call_once适合单例初始化因为它保证callable仅执行一次且线程可见,比双检锁更简洁安全;std::once_flag必须为static或全局,否则失效;异常会永久标记flag并重抛。

c++中如何使用std::call_once实现单例模式初始化_c++线程安全【实例】

std::call_once 为什么适合单例初始化

因为 std::call_once 保证传入的 callable 只被执行一次,且对所有线程可见——即使多个线程同时调用,也仅有一个能真正执行初始化逻辑,其余阻塞等待完成。它比手动加锁 + double-checked locking 更简洁、更不易出错,底层依赖 std::once_flag 的原子状态管理。

std::once_flag 必须是静态或全局生命周期

如果把 std::once_flag 声明在函数内部但非 static,每次调用都会构造新对象,std::call_once 就失去“只执行一次”的语义,导致重复初始化甚至未定义行为。

  • std::once_flag 必须是 static(推荐)、全局变量,或类的 static 成员
  • 不能是局部自动变量,也不能是堆上 new 出来的(除非你手动管理其生命周期并确保唯一性)
  • C++11 起支持 constexpr 初始化:static std::once_flag flag; 是合法且最常用写法

完整线程安全单例实现(带懒加载

以下是一个典型、可直接复用的 C++11 单例模板,使用 std::call_oncestd::once_flag 实现线程安全的延迟初始化:

class Singleton {
public:
    static Singleton& instance() {
        std::call_once(init_flag, []() {
            instance_ptr = new Singleton();
        });
        return *instance_ptr;
    }
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

private: Singleton() = default; // 可含复杂初始化逻辑 static Singleton* instance_ptr; static std::once_flag init_flag; };

// 定义静态成员(必须在 .cpp 中或内联于头文件末尾) Singleton* Singleton::instance_ptr = nullptr; std::once_flag Singleton::init_flag;

注意:instance_ptr 必须是 static,且初始化为 nullptrinit_flag 同样需定义一次。若放在头文件中,要加 inline(C++17)或确保 ODR 合规。

Originality AI
Originality AI

专门为网络出版商设计的抄袭和AI检测工具

下载

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

常见错误:忘记定义静态成员或误用局部 once_flag

编译期或运行期可能遇到这些现象:

  • 链接错误 undefined reference to 'Singleton::init_flag' → 忘记在 .cpp 中定义静态成员
  • 多次构造实例,日志打印两次 → std::once_flag flag; 写在 instance() 函数内部非 static 位置
  • 程序卡死或死锁 → 在初始化 lambda 中又调用了其他依赖本单例的函数(形成循环初始化),std::call_once 不检测这种逻辑环
  • 析构顺序问题 → 单例依赖全局对象,而该对象可能已销毁;建议避免在单例析构器中访问其他静态资源

真正容易被忽略的是:一旦 std::call_once 中抛出异常,该 std::once_flag 永远标记为“已尝试”,后续调用会直接 rethrow 同一个异常——所以初始化逻辑里务必捕获并处理异常,或确保不抛异常。

相关专题

更多
全局变量怎么定义
全局变量怎么定义

本专题整合了全局变量相关内容,阅读专题下面的文章了解更多详细内容。

78

2025.09.18

python 全局变量
python 全局变量

本专题整合了python中全局变量定义相关教程,阅读专题下面的文章了解更多详细内容。

96

2025.09.18

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

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

53

2025.08.29

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

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

101

2025.10.23

lambda表达式
lambda表达式

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

206

2023.09.15

python lambda函数
python lambda函数

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

191

2025.11.08

Python lambda详解
Python lambda详解

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

50

2026.01.05

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

394

2023.07.18

c++ 根号
c++ 根号

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

45

2026.01.23

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
nginx浅谈
nginx浅谈

共15课时 | 0.8万人学习

前端开发(基础+实战项目合集)
前端开发(基础+实战项目合集)

共60课时 | 3.9万人学习

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

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