0

0

c++如何使用std::shared_ptr_c++引用计数智能指针详解【核心】

裘德小鎮的故事

裘德小鎮的故事

发布时间:2026-02-08 13:53:02

|

478人浏览过

|

来源于php中文网

原创

std::shared_ptr通过独立控制块管理引用计数,强引用归零时析构对象并释放控制块;make_shared更高效且防异常泄漏,但需避免裸指针重复构造、循环引用及this误用。

c++如何使用std::shared_ptr_c++引用计数智能指针详解【核心】

std::shared_ptr 是怎么管理引用计数的

它不是靠指针本身存计数,而是额外分配一块控制块(control block),里面放引用计数、弱引用计数、析构器、分配器等信息。每次拷贝 std::shared_ptr,控制块里的强引用计数就 +1;每次离开作用域或被赋值为 nullptr,就 -1;减到 0 时,自动 delete 所管理的对象,并释放控制块。

  • 控制块和对象可以分开分配(默认行为),也可能合并在一次 new 中(用 std::make_shared 时)——后者更高效,但要注意:如果对象构造抛异常,控制块仍会泄漏(C++17 起已修复)
  • 引用计数操作是原子的,所以 std::shared_ptr 可在多线程中安全拷贝/赋值,但指向的 *对象本身不自动线程安全*
  • 不要混用裸指针初始化:int* p = new int(42); auto sp1 = std::shared_ptr(p); auto sp2 = std::shared_ptr(p); ——这会创建两个独立控制块,双重 delete

什么时候必须用 std::make_shared 而不是 new + 构造函数

绝大多数情况都该用 std::make_shared:它把对象和控制块一次分配,避免两次内存申请,也杜绝了因异常导致的资源泄漏风险(比如构造函数抛异常时,裸 new 分配的内存可能没被接管)。

  • 例外:需要自定义删除器且不能默认构造(std::make_shared 不接受删除器参数),此时只能用 std::shared_ptr(new T, my_deleter)
  • 注意:不能对数组用 std::make_shared() ——C++17 前不支持,即使支持,std::shared_ptr 也不带 [] 删除语义,应改用 std::shared_ptr + 自定义删除器
  • std::make_shared 完转发参数给 T 的构造函数,完美支持 explicit 构造、初始化列表、移动语义

std::shared_ptr 和 std::weak_ptr 配合破循环引用的关键点

循环引用不是“两个 shared_ptr 互相持有”,而是“两个对象通过 shared_ptr 成员彼此持有对方”,导致引用计数永远不归零。解决方式是其中一方改用 std::weak_ptr ——它不增加强引用计数,只在需要时调用 lock() 升级为 shared_ptr

  • std::weak_ptrlock() 返回 std::shared_ptr,若原对象已被销毁,则返回空 shared_ptr;别直接解引用 weak_ptr,它没有 operator->
  • 判断是否过期:用 wptr.expired()wptr.lock() != nullptr 稍快,但语义一致;真正使用前仍建议先 lock() 再判空,避免竞态
  • 注意:std::weak_ptr 本身也参与控制块的弱引用计数,控制块生命周期由强+弱引用共同决定;只有强引用为 0 且弱引用也为 0 时,控制块才释放

常见崩溃和误用场景(附错误信息提示)

很多 segfault 或 double-free 其实源于对控制块生命周期或所有权转移的理解偏差。

DeepSider
DeepSider

浏览器AI侧边栏对话插件,集成多个AI大模型

下载

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

  • 崩溃点:对已 reset 的 shared_ptr 调用 get() 后解引用 → nullptr 解引用,报 Segmentation fault (core dumped)
  • 崩溃点:用同一个裸指针重复初始化多个 shared_ptr → 控制块独立,最终 double free,报 double free or corruption
  • 陷阱:this 构造 shared_ptr(如 std::shared_ptr(this))——除非类继承自 std::enable_shared_from_this 并用 shared_from_this(),否则极大概率重复管理
  • 性能坑:频繁调用 use_count() ——它是原子读,但无必要;仅用于调试,禁止用于逻辑分支(如 if (sp.use_count() == 1)

控制块的存在形式、跨线程安全性边界、以及 weak_ptr 的“临时升级”机制,是理解 std::shared_ptr 行为差异的核心;实际写代码时,优先用 make_shared,禁用裸指针构造,循环引用必须显式拆解——这些不是最佳实践建议,而是避免 undefined behavior 的硬性约束。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

798

2023.08.22

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

626

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

552

2024.08.29

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

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

173

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

205

2025.08.29

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

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

173

2025.08.29

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

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

104

2025.10.23

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

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

613

2023.08.10

Golang处理数据库错误教程合集
Golang处理数据库错误教程合集

本专题整合了Golang数据库错误处理方法、技巧、管理策略相关内容,阅读专题下面的文章了解更多详细内容。

61

2026.02.06

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 8.8万人学习

C 教程
C 教程

共75课时 | 4.6万人学习

C++教程
C++教程

共115课时 | 16.4万人学习

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

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