为什么 C++ 中不能随便传裹指针?
C++ 中突出的智能指针 std::unique_ptr 在实际开发中很容易得到使用,但同时也有一些系统性的误用隐患。
这篇文章打算完整理一下:
-
为什么 把指针传给别人 时传
unique_ptr比传裹指针更安全? -
你传裹指针,实际上就是在与系统的负责纠缠!
一、把指针传给别人,传裹指针不是啊!
【可行但不建议】
把裹指针传给一个函数,就像把孩子送走了但什么补给都没有一样
例如:
std::unique_ptr<MyObject> obj = std::make_unique<MyObject>();
b->Register(obj.get()); // 传的是裹指针
在这个场景中,b 存储了 obj ,如果它后续 delete 了,那么一旦 obj 重复释放,程序立即 double delete 崩毁。
【推荐】:明确表达所有权,用 unique_ptr 传递
b->Register(std::move(obj)); // 显式所有权移交
并且在 B 类里面:
void Register(std::unique_ptr<MyObject> obj) {
obj_ = std::move(obj);
}
二、比较两种写法
【A 做事, B 接管(建议)】
// A.cpp
std::unique_ptr<MyObject> obj = std::make_unique<MyObject>();
b->Register(std::move(obj));
// B.h
void Register(std::unique_ptr<MyObject> obj);
// B.cpp
void B::Register(std::unique_ptr<MyObject> obj) {
obj_ = std::move(obj);
}
【A 保持拥有, B 并不接管(可行】】
// B.h
void Notify(MyObject* obj); // 不保存,也不释放
// A.cpp
b->Notify(obj.get());
三、别通传裹指针,很易的三个缺陷
Ὢ8 惊悚地一:裹指针 + delete
void B::Register(MyObject* obj) {
obj_ = obj;
// ...
delete obj_; // 很可能被 A 释放过,造成重复释放
}
Ὢ8 惊悚地二:存了, A 先释放
MyObject* obj = new MyObject();
b->Register(obj);
// ...
delete obj; // B 里面仍然使用 obj
这种场景为黄金缺陷——指针指向的地址成了垃圾,也就是所谓的 懒缓指针 / 悬堆指针 (dangling pointer)。
Ὢ8 惊悚地三:没人释放导致内存泄露
MyObject* obj = new MyObject();
b->Register(obj); // B 没有 delete ,A 也没有
四、要抽象,就看这表
| 特性 | 裹指针 (T*) | std::unique_ptr<T> |
|---|---|---|
| 是否表达所有权? | ❌ 否 | ✅ 是 |
| 是否自动释放? | ❌ 需手动 delete | ✅ 自动析构释放 |
| 是否可移动? | ✅ 是,但无语义约束 | ✅ 显式 move 转移所有权 |
| 易错程度 | ⚠️ 高,易泄露/重复释放 | ✅ 安全,编译期保障 |
第五、如果你真需要 "保存而不释放",怎么办?
你可以用
raw_ptr(Chromium 里用 base::raw_ptr) 或者observer pattern
-
无所有权关系,通知你自己已释放
-
你自己跟踪调用者的生命周期
总结
“传裹指针有问题吗?”
回答是:把负责交给了别人,却不报告调用意图,究竟谁该 delete?
用 unique_ptr 則是将负责全部传达。这是环境安全编程的基础。


被折叠的 条评论
为什么被折叠?



