深拷贝操作引发Double free问题的两类技术疑问
Line类双释放问题及相关疑问解答
原代码中Line pCopy; pCopy = pLine->deep_copy();会触发Double free错误,核心原因是默认赋值运算符的浅拷贝导致指针被重复释放。以下针对两个疑问逐一解答:
疑问1:Line pCopy = pLine->deep_copy();与原写法的差异
原写法执行流程
Line pCopy;:调用默认构造函数,pCopy的start和end被初始化为nullptr。pCopy = pLine->deep_copy();:- 先执行
deep_copy(),创建两个新的Point对象,返回一个临时Line对象(持有这两个新指针)。 - 调用默认赋值运算符,将临时对象的
start和end指针直接赋值给pCopy(浅拷贝)。 - 临时对象生命周期结束,调用析构函数
delete掉这两个Point指针,导致pCopy的指针变成野指针。 - main函数结束时,pCopy的析构函数再次
delete野指针,触发Double free错误。
- 先执行
修复写法执行流程
Line pCopy = pLine->deep_copy();属于拷贝初始化:
- 编译器会直接用
deep_copy()返回的结果构造pCopy(C17及以后标准强制消除临时对象的拷贝,直接在pCopy的内存空间构造返回值;C17前多数编译器也会做返回值优化RVO,跳过临时对象的创建)。 - 整个过程中只有pCopy持有新创建的
Point指针,临时对象不会被创建(或被优化掉),因此只有pCopy销毁时会调用一次析构函数,不会出现重复释放。
疑问2:实现拷贝构造函数后,修复写法调用拷贝构造而非直接使用deep_copy()结果的原因及解决方法
原因
Line pCopy = pLine->deep_copy();的拷贝初始化语法,在C++17之前的语义是:
- 先执行
deep_copy()得到临时Line对象。 - 调用拷贝构造函数,将临时对象的内容拷贝到pCopy中。
即使编译器做RVO优化,这只是编译器的行为,并非标准强制要求。当你自定义了拷贝构造函数后,编译器会严格按照语法规则调用它(除非触发优化)。
而C++17及以后,标准强制要求复制消除,此时编译器会直接在pCopy的内存空间构造deep_copy()的返回值,不会调用拷贝构造函数。
解决方法
- 升级到C++17或更高版本:利用标准强制的复制消除,直接跳过拷贝构造函数,将
deep_copy()的结果直接构造到pCopy中。 - 定义移动构造函数:如果无法升级编译器,可给
Line类添加移动构造函数,让临时对象的指针所有权直接转移给pCopy,避免拷贝:
此时Line(Line&& other) noexcept : start(other.start), end(other.end) { other.start = nullptr; other.end = nullptr; }Line pCopy = pLine->deep_copy();会调用移动构造函数,将临时对象的指针转移后,临时对象的析构函数不会释放指针(因为已经被置为nullptr),既避免了拷贝,也不会触发Double free。 - 改用直接初始化语法:写法上更清晰,本质和拷贝初始化一致:
Line pCopy(pLine->deep_copy());
内容的提问来源于stack exchange,提问作者Hossam Elsamanoudy
相关产品推荐
相关产品推荐

