c++如何实现单例模式_c++ Singleton模式写法【源码】
发布时间 - 2026-01-31 00:00:00 点击率:次C++单例不能只靠静态局部变量,因C++11仅保证其初始化线程安全,构造异常会导致重复进入,析构顺序不可控易引发崩溃;推荐静态局部变量+删除拷贝操作,兼顾简洁、线程安全与自动内存管理。
为什么 C++ 单例不能只靠静态局部变量?
因为线程安全不是默认的——C++11 起 static 局部变量的初始化才保证首次调用时的线程安全,但仅限于初始化过程;若构造函数里有耗时操作或抛异常,getInstance() 可能被多次进入,导致未定义行为。更麻烦的是,析构顺序不可控,全局对象析构时若其他单例还在用它,就崩了。
常见错误现象:std::terminate、访问已析构对象、多线程下重复构造、程序退出时崩溃。
- 必须用
static std::unique_ptr或双重检查锁定(DCLP)来控制生命周期 - 禁止在构造函数中调用其他单例的
getInstance() - 若需明确析构时机(比如日志单例要最后销毁),得手动调用
reset()
最简安全写法:C++11 静态局部变量 + delete 构造函数
这是目前推荐的默认方案,代码少、线程安全、自动管理内存,适用于绝大多数场景。
class Logger {
public:
static Logger& getInstance() {
static Logger instance; // C++11 线程安全初始化
return instance;
}
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
private:
Logger() = default; // 私有构造,防止外部 new
~Logger() = default; // 析构也私有,但允许静态对象调用
};
注意:static Logger instance 在第一次调用 getInstance() 时构造,程序结束前自动析构;若构造函数抛异常,下次调用仍会重试——这可能是你想要的,也可能不是。
需要手动控制析构?用 std::unique_ptr + call_once
当单例依赖其他资源(如文件句柄、网络连接),且必须在所有其他单例析构后才关闭时,就得自己管生命周期。
关键点:std::call_once 保证只初始化一次,std::unique_ptr 控制堆内存和析构时机。
class Config {
public:
static Config& getInstance() {
std::call_once(initFlag, []() {
instance.reset(new Config);
});
return *instance;
}
static void destroy() { instance.reset(); } // 显式销毁
private:
Config() = default;
static std::unique_ptr instance;
static std::once_flag initFlag;
};
instance.reset() 会立即调用析构函数;destroy() 必须在所有依赖它的对象销毁后调用,否则后续访问就是悬空引用。
性能影响:比静态局部变量多一次指针解引用和一次 call_once 检查,但几乎可忽略;兼容性好,C++11 起都支持。
别踩坑:std::shared_ptr 不适合做单例句柄
有人用 static std::shared_ptr 返回 shared_ptr,这会导致两个问题:
- 每次调用
getInstance()都产生新引用,无法判断单例是否已被销毁 - 多个
shared_ptr实例可能延长对象生命周期,违背“全局唯一且可控”的本意
正确做法是始终返回引用(T&)或原始指针(T*),让使用者清楚:这不是一个可共享所有权的对象,而是一个全局服务入口。
另外,模板单例(如 Singleton)看似通用,但容易掩盖类型耦合和初始化顺序问题,除非你真需要几十个不同类型的单例且能严格约束它们的依赖图,否则不建议一开始就上。
# c++
# 为什么
# red
# Static
# 构造函数
# 析构函数
# 局部变量
# 指针
# 堆
# 线程
# 多线程
# delete
# 对象
# 只靠
# 的是
# 是一个
# 这是
# 还在
# 首次
# 多个
# 句柄
# 已被
# 适用于
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?
音响网站制作视频教程,隆霸音响官方网站?
标题:Vue + Vuex + JWT 身份认证的正确实践与常见误区解析
Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】
网站设计制作书签怎么做,怎样将网页添加到书签/主页书签/桌面?
如何在阿里云购买域名并搭建网站?
Laravel如何实现模型的全局作用域?(Global Scope示例)
Laravel怎么处理异常_Laravel自定义异常处理与错误页面教程
如何挑选高效建站主机与优质域名?
昵图网官网入口 昵图网素材平台官方入口
Laravel如何处理异常和错误?(Handler示例)
Laravel怎么实现搜索功能_Laravel使用Eloquent实现模糊查询与多条件搜索【实例】
如何快速搭建高效简练网站?
Java遍历集合的三种方式
Laravel数据库迁移怎么用_Laravel Migration管理数据库结构的正确姿势
晋江文学城电脑版官网 晋江文学城网页版直接进入
javascript日期怎么处理_如何格式化输出
Python进程池调度策略_任务分发说明【指导】
Laravel如何发送系统通知_Laravel Notifications实现多渠道消息通知
黑客如何利用漏洞与弱口令入侵网站服务器?
奇安信“盘古石”团队突破 iOS 26.1 提权
Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】
如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南
javascript中的数组方法有哪些_如何利用数组方法简化数据处理
如何在HTML表单中获取用户输入并用JavaScript动态控制复利计算循环
如何实现javascript表单验证_正则表达式有哪些实用技巧
VIVO手机上del键无效OnKeyListener不响应的原因及解决方法
Laravel如何使用Spatie Media Library_Laravel图片上传管理与缩略图生成【步骤】
Laravel API路由如何设计_Laravel构建RESTful API的路由最佳实践
C#如何调用原生C++ COM对象详解
Laravel Asset编译怎么配置_Laravel Vite前端构建工具使用
,交易猫的商品怎么发布到网站上去?
EditPlus中的正则表达式 实战(2)
如何快速搭建高效服务器建站系统?
如何正确下载安装西数主机建站助手?
如何在IIS中新建站点并配置端口与物理路径?
DeepSeek是免费使用的吗 DeepSeek收费模式与Pro版本功能详解
如何在阿里云虚拟服务器快速搭建网站?
如何使用 Go 正则表达式精准提取括号内首个纯字母标识符(忽略数字与嵌套)
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
html5怎么画眼睛_HT5用Canvas或SVG画眼球瞳孔加JS控制动态【绘制】
,网页ppt怎么弄成自己的ppt?
Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】
Laravel怎么导出Excel文件_Laravel Excel插件使用教程
如何在万网自助建站中设置域名及备案?
如何快速搭建FTP站点实现文件共享?
如何获取PHP WAP自助建站系统源码?
网站制作软件免费下载安装,有哪些免费下载的软件网站?
Laravel如何升级到最新版本?(升级指南和步骤)
zabbix利用python脚本发送报警邮件的方法


