0

0

c++的std::unique_ptr和std::shared_ptr之间如何转换? (所有权转移)

尼克

尼克

发布时间:2026-01-10 14:07:17

|

405人浏览过

|

来源于php中文网

原创

std::unique_ptr转std::shared_ptr只能通过std::move实现所有权转移,直接用.get()构造会导致双重释放;反之不可行,因shared_ptr共享所有权而unique_ptr要求独占,且use_count()==1非线程安全。

c++的std::unique_ptr和std::shared_ptr之间如何转换? (所有权转移)

std::unique_ptr 转 std::shared_ptr:只能 move,不能 copy

std::unique_ptr 本质是独占所有权,无法复制;要转成 std::shared_ptr,必须交出控制权,用 std::move() 转移底层指针。这是唯一安全的方式。

  • 直接构造 std::shared_ptr 时传入已 move 的 std::unique_ptr,会接管其资源并初始化引用计数为 1
  • std::unique_ptr 在 move 后变为 nullptr,再访问会 UB(未定义行为)
  • 不能用 .get() + 原始指针构造 std::shared_ptr —— 这会导致双重 delete(unique_ptr 析构时仍尝试释放)
std::unique_ptr up = std::make_unique(42);
std::shared_ptr sp{std::move(up)}; // ✅ 正确:所有权转移
// std::shared_ptr sp2{up.get()}; // ❌ 危险:up 之后析构会重复释放

std::shared_ptr 转 std::unique_ptr:通常不可行,除非你确定只剩一个引用

因为 std::shared_ptr 表示共享所有权,而 std::unique_ptr 要求独占,所以标准库不提供直接转换接口。强行“转”需要先确认引用计数为 1,再用 .release() 拿出裸指针手动构造 —— 但这个过程不安全,且破坏 RAII。

  • sp.use_count() == 1 是必要前提,但不是线程安全的判断条件(竞态下可能刚检查完就新增引用)
  • sp.reset()sp = nullptr 后,sp.get() 变为 nullptr,但此时资源已被释放,不能再用于构造 unique_ptr
  • 真正能“等效替代”的做法是:改用 std::shared_ptr 并接受共享语义,或重构生命周期,避免中途切换

为什么没有隐式或显式转换函数?

这是设计上的刻意限制,反映两种智能指针的根本差异:

  • std::unique_ptrstd::shared_ptr:move 是单向、明确的所有权移交,语义清晰
  • std::shared_ptrstd::unique_ptr:意味着“我断定此刻没人跟我共享”,但 C++ 无法在运行时可靠验证这一点,强制转换会掩盖潜在竞态或误用
  • 标准库不提供 shared_ptr::release(),正是因为释放后无法保证其他副本不继续使用资源

常见错误场景和替代思路

实际开发中,想“转”往往说明接口设计或所有权边界没理清。更稳妥的做法是:

ImgGood
ImgGood

免费在线AI照片编辑器

下载

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

  • 函数返回 std::unique_ptr,调用方按需转为 std::shared_ptr(如需长期持有或跨线程)
  • 避免让同一对象既被 unique_ptr 又被 shared_ptr 管理 —— 容易引发 double-free 或悬空指针
  • 若需灵活切换,考虑用 std::shared_ptr 统一管理,配合 weak_ptr 规避循环引用
  • 极少数确定单引用场景(如工厂内部临时封装),可用 std::shared_ptr::get() + std::unique_ptr 自定义 deleter 空操作,但这属于 hack,不推荐

所有权转移不是类型转换,而是语义承诺。搞错方向,轻则内存泄漏,重则崩溃 —— 尤其在多线程或复杂生命周期中,use_count() 的瞬时值根本靠不住。

相关专题

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

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

52

2025.08.29

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

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

98

2025.10.23

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1018

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

63

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

405

2025.12.29

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

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

480

2023.08.10

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

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

143

2025.12.24

空指针异常处理
空指针异常处理

本专题整合了空指针异常解决方法,阅读专题下面的文章了解更多详细内容。

22

2025.11.16

Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

8

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号