shared_ptr初始化不能用裸指针直接构造,因其构造函数为explicit,禁止隐式转换;且裸指针构造易致异常时内存泄漏、重复释放或引用计数错乱,必须优先使用std::make_shared。

shared_ptr 初始化时为什么不能用裸指针直接构造
因为 shared_ptr 的核心是引用计数,它需要在构造时接管资源的生命周期管理。如果用裸指针(比如 new MyClass())直接传给 shared_ptr 构造函数,而该指针已被其他 shared_ptr 持有,就会导致双重释放或计数错乱——这是典型的未定义行为。
正确做法是:优先用 std::make_shared,它原子性地分配对象和控制块,效率更高、更安全;实在要用裸指针,必须确保该指针从未被其他智能指针管理过,且只构造一次:
auto p1 = std::make_shared<int>(42); // ✅ 推荐 auto p2 = std::shared_ptr<int>(new int(42)); // ⚠️ 不推荐,可能异常安全问题</int></int>
-
make_shared在单次内存分配中同时创建控制块和对象,减少开销 - 裸指针构造方式在构造过程中若抛异常(如
shared_ptr控制块分配失败),裸指针会泄漏 - 禁止把同一裸指针传给多个
shared_ptr构造函数,哪怕只是临时变量
shared_ptr 赋值和拷贝时引用计数怎么变化
shared_ptr 是可拷贝的,每次拷贝或赋值都会使内部引用计数 +1;析构或重置时 -1;计数归零时自动 delete 所管理的对象。
注意:这不是“深拷贝”,而是共享所有权。以下操作都增加引用计数:
auto a = std::make_shared<:string>("hello");
auto b = a; // 计数变为 2
auto c = std::move(a); // a 变为空,c 接管,计数仍为 2(b 和 c 共享)</:string>
- 拷贝构造、赋值、函数传值参数、容器插入(如
std::vector<shared_ptr>></shared_ptr>)都会增加计数 - 移动操作(
std::move)不改变计数,只转移控制权 - 循环引用场景下(A 持有 B 的
shared_ptr,B 也持有 A 的),计数永远不为 0,必须用weak_ptr打破
如何安全访问 shared_ptr 管理的对象
不要解引用空的 shared_ptr,也不要假设它一定非空。访问前应检查,或使用安全的解引用方式。
常见错误是直接写 *ptr 或 ptr->method() 而没判空,一旦 ptr 为空(比如被 reset() 过),就会崩溃。
- 用
if (ptr)或if (ptr != nullptr)判空(shared_ptr重载了operator bool) - 避免裸指针转换:
ptr.get()返回原始指针,但不延长生命周期,也不影响计数,慎用 - 不要长期保存
ptr.get()的结果,它可能在别处被释放 - 成员函数调用建议写成
ptr ? ptr->foo() : fallback,而不是先assert(ptr)
shared_ptr 和 unique_ptr 混用时要注意什么
不能把同一个裸指针分别交给 shared_ptr 和 unique_ptr 管理,也不能让两者间接共享同一资源——这会导致未定义行为,通常表现为 double-free。
典型陷阱是:函数返回 unique_ptr,你把它转成 shared_ptr 后又保留原 unique_ptr:
auto up = std::make_unique<int>(10); auto sp = std::shared_ptr<int>(up.release()); // ✅ 正确:移交所有权 // auto sp = std::shared_ptr<int>(up.get()); // ❌ 错误:up 仍持有,sp 不知道</int></int></int>
-
unique_ptr转shared_ptr必须用std::move或release(),不能用.get() -
shared_ptr无法转回unique_ptr(所有权已共享,无法独占) - 跨模块传递时,明确约定所有权语义:谁 new,谁 delete;谁负责生命周期——用智能指针类型本身传递意图
shared_ptr、什么时候该用 unique_ptr 或直接传引用;以及在多线程环境下,shared_ptr 的控制块线程安全(计数增减是原子的),但所指对象本身完全不保证线程安全——这点最容易被忽略。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











