如何为动态数组或uni_ptr编写移动构造函数与移动赋值运算符
问题1:移动构造函数与移动赋值运算符实现
你原有代码的移动语义核心思路是正确的,核心就是转移动态数组的所有权、将被移动对象的指针置空避免双重释放,只需修正原有代码的几处错误即可:
- 构造函数中
std::fill的结束指针计算错误:指针算术以元素为单位,不需要乘以sizeof(Tile),且原有代码存在笔误漏写了_cols_和sizeof之间的乘号 - 拷贝赋值运算符漏写返回值类型
Grid&,且未提前释放原有数组,存在内存泄漏 - 移动赋值运算符未做自赋值校验,自赋值场景下会提前释放数组导致异常
- 原有代码漏写析构函数释放动态数组,会造成内存泄漏
修正后的完整移动相关代码如下:
// 移动构造函数(size_t是基础类型,std::move可省略不影响逻辑) Grid(Grid &&other) noexcept : _rows_(other._rows_), _cols_(other._cols_), _initialTile_(other._initialTile_), mytiles(other.mytiles) { // 清空被移动对象,避免双重释放 other.mytiles = nullptr; other._rows_ = other._cols_ = 0; } // 移动赋值运算符(补充自赋值校验) Grid& operator=(Grid &&other) noexcept { if (this == &other) { return *this; } // 释放原有数组 delete[] mytiles; // 转移所有权 _rows_ = other._rows_; _cols_ = other._cols_; _initialTile_ = other._initialTile_; mytiles = other.mytiles; // 清空被移动对象 other.mytiles = nullptr; other._rows_ = other._cols_ = 0; return *this; } // 补充析构函数 ~Grid() { delete[] mytiles; }
另外你原有的拷贝赋值运算符也需要修正内存泄漏问题:
Grid& operator=(const Grid &other) { if (this == &other) { return *this; } // 先释放原有内存 delete[] mytiles; _rows_ = other._rows_; _cols_ = other._cols_; _initialTile_ = other._initialTile_; mytiles = new Tile[other._rows_ * other._cols_]; std::copy(other.mytiles, other.mytiles + other._rows_ * other._cols_, mytiles); return *this; }
问题2:改用std::unique_ptr实现动态数组
你提到的uni_ptr是std::unique_ptr的笔误,针对动态数组需要使用std::unique_ptr的数组特化版本std::unique_ptr<Tile[]>,它会自动调用delete[]释放内存,无需手动管理内存、编写析构函数,且默认生成的移动构造/移动赋值可直接使用,大幅降低出错概率。
替换后的完整Grid类代码如下:
#include <memory> #include <algorithm> enum Tile { See, Sky, Road }; class Grid { private: size_t _rows_ = 0; size_t _cols_ = 0; Tile _initialTile_; // 替换为数组特化的unique_ptr std::unique_ptr<Tile[]> mytiles; public: Grid(size_t rows, size_t cols, const Tile &initialTile) : _rows_(rows), _cols_(cols), _initialTile_(initialTile) { mytiles = std::make_unique<Tile[]>(_cols_ * _rows_); std::fill(mytiles.get(), mytiles.get() + _rows_ * _cols_, _initialTile_); } // 拷贝构造需要手动实现,因为unique_ptr不支持拷贝 Grid(const Grid &other) : _rows_(other._rows_), _cols_(other._cols_), _initialTile_(other._initialTile_) { mytiles = std::make_unique<Tile[]>(other._rows_ * other._cols_); std::copy(other.mytiles.get(), other.mytiles.get() + other._rows_ * other._cols_, mytiles.get()); } // 拷贝赋值需要手动实现 Grid& operator=(const Grid &other) { if (this == &other) { return *this; } _rows_ = other._rows_; _cols_ = other._cols_; _initialTile_ = other._initialTile_; mytiles = std::make_unique<Tile[]>(other._rows_ * other._cols_); std::copy(other.mytiles.get(), other.mytiles.get() + other._rows_ * other._cols_, mytiles.get()); return *this; } // 移动构造和移动赋值直接用默认生成的即可,unique_ptr原生支持移动语义 Grid(Grid &&other) noexcept = default; Grid& operator=(Grid &&other) noexcept = default; // 无需手动写析构函数,unique_ptr会自动释放内存 ~Grid() = default; };
替换后访问数组元素的方式和原有裸指针完全一致,直接用mytiles[index]即可,不需要修改其他业务逻辑。
内容的提问来源于stack exchange,提问作者wangmyde
相关产品推荐
相关产品推荐

