含分配指针的类的拷贝构造正确实现及浅拷贝问题求解
问题描述
我有一个包含动态分配指针的类(示例中简化为1个指针),希望调用拷贝构造时无需执行深拷贝。该类定义如下:
class Example { public: Example() : ptr1(0), len1(0) {} Example(const Example& other) : ptr1(0), len1(0) { if (other.ptr1) { ptr1 = other.ptr1; len1 = other.len1; } } ~Example() { if (ptr1) delete[] ptr1; } char* ptr1; int len1; };
当创建该类实例、分配指针后插入到函数外作用域的容器时,会出现合理的use after free问题:
// some function { Example a; a.ptr1 = new char[1]; vec.push_back(a); // 注:vec是函数外创建的std::vector<Example> }
我知道深拷贝可以解决此问题,或插入空实例后操作容器中保存的拷贝,但想了解C++11标准下是否有更多解决方案。
C++11下的替代解决方案
1. 实现移动语义转移资源所有权
C++11的移动语义可以直接转移资源所有权,避免拷贝操作。需要为Example类添加移动构造函数和移动赋值运算符:
// 移动构造函数 Example(Example&& other) noexcept : ptr1(other.ptr1), len1(other.len1) { // 清空源对象指针,防止其析构时释放资源 other.ptr1 = nullptr; other.len1 = 0; } // 移动赋值运算符 Example& operator=(Example&& other) noexcept { if (this != &other) { // 释放当前对象已有资源 if (ptr1) delete[] ptr1; // 转移源对象的资源所有权 ptr1 = other.ptr1; len1 = other.len1; // 清空源对象 other.ptr1 = nullptr; other.len1 = 0; } return *this; }
使用时通过std::move将局部对象的资源转移给容器元素:
vec.push_back(std::move(a));
此时局部对象a析构时,因指针已被置空不会释放资源,容器中的对象持有资源所有权,彻底避免double free和use after free问题。
2. 用智能指针替代裸指针
C++11提供的智能指针可以自动管理资源生命周期,无需手动编写析构、拷贝/移动逻辑:
- 共享资源场景:用
std::shared_ptr,拷贝时自动维护引用计数,只有最后一个持有资源的对象析构时才释放内存:
#include <memory> class Example { public: Example() : len1(0) {} Example(const Example& other) = default; ~Example() = default; std::shared_ptr<char[]> ptr1; int len1; };
- 独占资源场景:用
std::unique_ptr,禁用拷贝操作,只能通过移动语义转移所有权:
#include <memory> class Example { public: Example() : len1(0) {} // 禁用拷贝构造和赋值 Example(const Example& other) = delete; Example& operator=(const Example& other) = delete; // 默认生成移动构造和赋值 Example(Example&& other) = default; Example& operator=(Example&& other) = default; ~Example() = default; std::unique_ptr<char[]> ptr1; int len1; };
插入容器时同样用std::move转移所有权:
vec.push_back(std::move(a));
3. 直接在容器内构造对象
使用emplace_back直接在容器的内存空间中构造Example对象,跳过局部对象的创建和析构过程:
// 为Example添加带参数的构造函数简化操作 Example(int length) : len1(length) { ptr1 = length > 0 ? new char[length] : nullptr; } // 直接在容器中构造对象 vec.emplace_back(1); // 构造一个len1=1、ptr1指向new char[1]的Example对象
这种方式从根源上避免了资源所有权转移的问题,无需处理局部对象的析构。
内容的提问来源于stack exchange,提问作者carce-bo
相关产品推荐
相关产品推荐

