如何避免C++析构函数中的double free问题?
问题分析与解决方案
问题根源:并非竞争条件,而是浅拷贝导致的重复释放
你的问题不是竞争条件(竞争条件多出现于多线程场景,由多个线程同时操作同一块内存引发),核心原因是浅拷贝:
UDPCPMessage类的拷贝构造函数和赋值运算符仅直接复制_udpcpChecksum指针,没有为新对象分配独立的内存空间。这导致原对象与拷贝生成的对象共享同一块堆内存:
- 当
main函数中的udpcp对象,以及sendAck函数里的形参对象(通过拷贝生成)各自析构时,都会调用delete _udpcpChecksum,对同一块内存执行两次释放,最终触发double free错误。
从程序输出也能验证:两次trying to delete对应两个对象的析构流程,最终引发内存释放冲突。
更优解决方案
1. 实现深拷贝
为拷贝构造函数和赋值运算符分配独立内存,确保每个对象拥有专属的UDPCPChecksum实例:
// 拷贝构造函数:为新对象创建独立的UDPCPChecksum实例 UDPCPMessage::UDPCPMessage(const UDPCPMessage& udpcpMessage) { cout << "copy constructor called" << endl; _udpcpChecksum = new UDPCPChecksum(*udpcpMessage._udpcpChecksum); } // 赋值运算符:遵循异常安全原则,先分配新内存再释放旧内存 UDPCPMessage& UDPCPMessage::operator=(const UDPCPMessage& udpcpMessage) { cout << "assignment called" << endl; if (this != &udpcpMessage) { // 避免自赋值导致的错误 // 先创建临时对象,防止new失败丢失旧内存 UDPCPChecksum* temp = new UDPCPChecksum(*udpcpMessage._udpcpChecksum); delete _udpcpChecksum; _udpcpChecksum = temp; } return *this; }
2. 使用智能指针替代裸指针
借助C++标准库的智能指针自动管理内存,无需手动编写析构、拷贝构造和赋值运算符:
- 如果
UDPCPChecksum的所有权仅属于单个UDPCPMessage,使用std::unique_ptr(独占所有权,默认禁用拷贝,从根源避免浅拷贝):
#include <memory> class UDPCPMessage { std::unique_ptr<UDPCPChecksum> _udpcpChecksum; int A; public: UDPCPMessage() : _udpcpChecksum(std::make_unique<UDPCPChecksum>()) { cout << "default constructor called" << endl; } UDPCPMessage(const std::string& msg) : _udpcpChecksum(std::make_unique<UDPCPChecksum>()) { cout << "parameter constructor called" << endl; } // 无需手动编写析构函数,unique_ptr会自动释放内存 };
- 如果需要多个对象共享
UDPCPChecksum,使用std::shared_ptr(通过引用计数管理内存,最后一个对象销毁时才释放):
#include <memory> class UDPCPMessage { std::shared_ptr<UDPCPChecksum> _udpcpChecksum; int A; public: UDPCPMessage() : _udpcpChecksum(std::make_shared<UDPCPChecksum>()) { cout << "default constructor called" << endl; } // 拷贝构造和赋值运算符会自动维护引用计数,无需手动实现 };
3. 禁止拷贝操作
如果UDPCPMessage对象不需要被拷贝,直接禁用拷贝构造和赋值运算符,提前阻断浅拷贝可能:
class UDPCPMessage { UDPCPChecksum* _udpcpChecksum; int A; public: UDPCPMessage(); UDPCPMessage(const std::string& msg); ~UDPCPMessage(); // 显式禁用拷贝构造和赋值运算符 UDPCPMessage(const UDPCPMessage&) = delete; UDPCPMessage& operator=(const UDPCPMessage&) = delete; };
此时若代码中出现拷贝UDPCPMessage的操作(比如sendAck按值传参),编译器会直接报错,提前发现问题。
内容的提问来源于stack exchange,提问作者Neeraj
相关产品推荐
相关产品推荐

