“谁 new 谁 delete”是错误直觉,raii 要求对象明确唯一承担资源生命周期责任;std::unique_ptr 以移动语义强制独占所有权,虚析构函数确保多态销毁完整性。

谁 new 的,谁 delete?这个直觉是错的
在 C++ 面向对象设计中,“谁分配谁释放”看似合理,但实际极易导致资源归属模糊。比如 new 出来的指针传给多个函数、被赋值给多个变量,或作为返回值传出后没人知道该由谁负责 delete —— 这时“所有者”就消失了。RAII 的核心不是把资源塞进某个类,而是**让一个对象明确且唯一地承担资源生命周期的全部责任**。
std::unique_ptr 是最直接的所有权声明方式
它用移动语义强制表达“独占所有权”,且编译期就拒绝复制。一旦你把裸指针包装成 std::unique_ptr,所有者就从隐式约定变成显式类型契约。
- 构造时移交所有权:
auto p = std::make_unique<resource>(...);</resource>—— 此时p是唯一所有者 - 函数参数用值传递
std::unique_ptr:表示“我要接管这个资源”,调用方失去所有权 - 函数返回
std::unique_ptr:表示“我把所有权交给你”,接收方必须用auto或显式类型承接 - 不能写
std::unique_ptr<t> q = p;</t>(编译报错),只能写auto q = std::move(p);—— 所有权转移清晰可见
类成员中持有资源时,析构函数必须是释放点,且禁止浅拷贝
如果一个类内部用裸指针或句柄管理资源(如 FILE*、int socket_fd),那这个类就是资源所有者。但仅靠析构函数还不够,必须切断非法复制路径:
- 显式删除拷贝构造函数:
MyClass(const MyClass&) = delete; - 显式删除拷贝赋值运算符:
MyClass& operator=(const MyClass&) = delete; - 析构函数里做空指针/无效句柄检查,再释放,最后置空(如
fp = nullptr;) - 若支持移动,需提供移动构造和移动赋值,并将源对象的资源句柄设为无效状态(如
other.fp = nullptr;)
基类指针指向派生类对象时,虚析构函数是所有权链完整的前提
当通过 Base* 指针管理 Derived 对象时,如果 Base::~Base() 不是 virtual,delete ptr; 只会调用 Base 的析构函数,派生类部分资源永远不释放 —— 这意味着“所有权”在多态场景下被截断了。
- 只要类设计为被继承、且可能通过基类指针销毁,就必须声明
virtual ~Base() = default;或自定义虚析构函数 - 虚析构函数本身不决定谁是所有者,但它确保:当所有者(比如智能指针或栈对象)执行销毁动作时,整个继承链的析构逻辑都能跑完
- 没有虚析构函数的基类,不应作为多态资源容器使用;否则,
std::shared_ptr<base>也无法正确释放派生类资源
所有权不是文档里的一句话,也不是注释里的“请勿 delete”,它是类型系统能强制约束的行为。最容易被忽略的是:即使用了 std::shared_ptr,如果控制块里没存对的删除器(比如用 delete 释放 malloc 内存),所有权语义依然崩坏——此时“所有者”只是个幻觉。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











