如何使用std::shared_ptr重写自定义动态数组TVector的拷贝构造函数
问题回顾
我实现了一个名为TVector的自定义动态数组类,原本的拷贝构造函数用malloc分配内存,但现在想改用std::shared_ptr来管理数组内存。尝试了下面的写法后发现不仅参数类型不对,还搞不清怎么用std::shared_ptr访问数组元素,没法完成拷贝:
原来的拷贝构造函数:
TVector::TVector(TVector &other) { _size = other.length(); _array = (Square*) malloc(sizeof other); for(int i = 0; i < other.length(); ++i){ _array[i] = other[i]; } }
我尝试的错误代码:
TVector::TVector(std::shared_ptr<TVector> other) { _size = other->length(); std::shared_ptr<Square[]> _array(nullptr); for(int i = 0; i < other->length(); ++i){ _array[i] = other[i]; } }
TVector类定义:
class TVector { public: TVector(); TVector(TVector& other); virtual ~TVector(); void Print(); void push_back(const Square& value); void pop_back(); void clear(); int length(); Square& Last(); Square& operator[](int idx); bool empty(); void resize(int count); void erase(size_t pos); friend std::ostream& operator<<(std::ostream& os, TVector& s); friend std::istream& operator>>(std::istream& is, TVector& p); int _size; private: Square *_array; };
关键问题分析
你的尝试里有三个核心问题:
- 拷贝构造函数参数类型错误:拷贝构造的作用是从已存在的
TVector对象创建新对象,参数应该是const TVector&(或非const引用),而非std::shared_ptr<TVector>——后者是用来管理TVector对象本身的智能指针,完全偏离了拷贝构造的语义。 - 数组版
std::shared_ptr使用不当:你需要先把类内的_array从Square*改为std::shared_ptr<Square[]>(数组特化版智能指针),C++17及以后这个版本支持直接用下标运算符[],旧标准可以通过get()获取原始指针再访问元素。 - 原代码隐藏bug:
malloc(sizeof other)是严重错误的——它分配的是TVector对象的大小,而非Square数组所需的内存,会导致内存不足、越界访问,用智能指针时必须分配对应元素数量的数组内存。
正确实现步骤
第一步:修改类成员变量
先把TVector的私有成员_array替换为数组版智能指针:
class TVector { // ... 其他成员保持不变 ... private: std::shared_ptr<Square[]> _array; // 替换原有的Square* _array; };
第二步:重写拷贝构造函数
遵循标准拷贝构造的签名,结合std::shared_ptr<Square[]>的用法实现:
// 参数改为const TVector&,符合"拷贝不修改源对象"的语义 TVector::TVector(const TVector& other) : _size(other._size) { // 用初始化列表初始化_size,更高效 // 方式1:直接new数组,交给shared_ptr管理 _array = std::shared_ptr<Square[]>(new Square[_size]); // 方式2:C++20及以后推荐用make_shared(更安全,避免潜在内存泄漏) // _array = std::make_shared<Square[]>(_size); // 逐个拷贝源对象的元素 for(int i = 0; i < _size; ++i){ // C++17+支持直接用shared_ptr<Square[]>的[]运算符 _array[i] = other._array[i]; // 若使用C++17之前的版本,用get()获取原始指针访问 // _array.get()[i] = other._array.get()[i]; } }
第三步:调整其他成员函数(必要补充)
由于_array改为智能指针,原有依赖原始指针的函数也需要调整,比如默认构造函数:
TVector::TVector() : _size(0), _array(nullptr) {}
push_back函数的示例调整:
void TVector::push_back(const Square& value) { // 分配新数组,大小为当前_size+1 std::shared_ptr<Square[]> new_array(new Square[_size + 1]); // 拷贝旧数组元素 for(int i = 0; i < _size; ++i){ new_array[i] = _array[i]; } // 添加新元素 new_array[_size] = value; // 转移智能指针所有权,避免数组拷贝 _array = std::move(new_array); _size++; }
补充说明
const TVector&参数的意义:使用const修饰参数,既符合拷贝操作不修改源对象的语义,也允许从const TVector对象创建新实例,是更标准的写法。- 数组版shared_ptr的优势:它会自动在最后一个引用消失时释放数组内存,无需手动调用
delete[],彻底避免内存泄漏风险。 - 原
malloc的问题:malloc不会调用Square的构造函数,直接对其分配的内存赋值属于未定义行为;用new或make_shared会自动调用Square的默认构造函数,再进行赋值操作,安全性更高。
内容的提问来源于stack exchange,提问作者Yan
相关产品推荐
相关产品推荐

