必须手动写析构函数当类持有需显式释放的资源(如new内存、文件描述符、互斥锁),否则必泄漏;拷贝构造与赋值须一起写以防浅拷贝导致重复释放;c++11后应补全移动操作以避免退化为拷贝;零法则适用仅含raii成员且无裸指针的情况。

什么时候必须手动写析构函数?
只要类里有需要显式释放的资源(比如 new 出来的内存、打开的文件描述符、持有的互斥锁),就必须写析构函数。不是“建议”,是“不写就泄漏”。
- 常见错误现象:
valgrind报内存泄漏,或程序运行一阵后崩溃——实际是资源耗尽,比如文件句柄用光 - 使用场景:封装 C 风格资源(
fopen/fclose)、管理裸指针、持有系统句柄(WindowsHANDLE、Linuxint fd) - 注意:析构函数不能抛异常(C++11 起默认为
noexcept),否则std::terminate直接终止程序
拷贝构造和拷贝赋值函数为什么常要一起写?
因为它们处理的是同一类问题:对象被复制时,资源怎么分?不写或只写一个,编译器生成的默认版本会做浅拷贝,两个对象指向同一块内存,析构时重复释放。
- 常见错误现象:
double free or corruption或segmentation fault,尤其在函数返回局部对象、容器扩容时高频触发 - 参数差异:
T(const T&)是构造,T& operator=(const T&)是赋值;后者必须检查自赋值(if (this == &other) return *this;) - 性能影响:深拷贝可能很慢;若资源不可复制(如 socket),应禁用拷贝(
= delete)
C++11 后移动语义怎么改写三法则?
移动构造和移动赋值不是可选项——只要类支持高效转移资源(比如把指针“偷”过来),就必须补上这两个函数,否则移动操作会退化成拷贝。
- 使用场景:返回大型对象(
std::vector、自定义缓冲区)、频繁插入/删除容器元素 - 容易踩的坑:移动后源对象必须处于“有效但未指定状态”,不能假设它为空或为零;常见错误是移动后还读
ptr_成员而没置空 - 典型写法:移动构造中用
std::move(other.ptr_)转移所有权,并立刻把other.ptr_ = nullptr
零法则:什么情况下可以一个特殊成员都不写?
当类里所有成员都满足“自动正确管理资源”时——也就是只用 std::string、std::vector、std::unique_ptr 这类 RAII 类型,且没有裸指针、C 资源或自定义析构逻辑。
立即学习“C++免费学习笔记(深入)”;
- 判断依据:删掉所有
~T()、T(const T&)、T(T&&)等函数后,代码仍能正确运行、无泄漏、无重复释放 - 兼容性影响:零法则类天然支持聚合初始化、
constexpr构造(如果成员也支持),且更易被编译器优化 - 警告信号:只要加了一个
int*成员,零法则就立刻失效;别指望编译器替你补逻辑
最常被忽略的点是:析构函数一旦存在,编译器就不再生成移动操作——哪怕你只写了 ~T(),也得手动加 T(T&&) 和 operator=(T&&) 才能获得移动语义。这不是设计选择,是标准硬性规定。









