在二维数组上实现move赋值运算符

C++: Implementing move assignment operator on two dimensional array

本文关键字:move 赋值运算符 实现 二维数组      更新时间:2023-10-16

我有一个用来实现矩阵的类,在这里:

template<typename Comparable>
class Matrix {
    private: 
        std::size_t num_cols_;
        std::size_t num_rows_;
        Comparable **array_;
    public:
        Matrix();
        ~Matrix(); // Destructor
        Matrix(const Matrix<Comparable> & rhs);// Copy constructor
        Matrix(Matrix<Comparable> && rhs); // Move constructor
        Matrix<Comparable> & operator= (const Matrix<Comparable> & rhs);// Copy assignment
        Matrix<Comparable> & operator= (Matrix<Comparable> && rhs); // Move assignment
        template<typename buh> friend std::ostream &operator<< (std::ostream &os, Matrix<buh> &rhs);
        void ReadMatrix();
};

(对于这个特定的问题,向量不是一个选项。)

array_成员保存矩阵本身,并使用以下代码填充:

array_ = new Comparable*[num_rows_];
    for (int i = 0; i < num_rows_; ++i) {
        array_[i] = new Comparable[num_cols_];
    };
    for(int i = 0;i < num_rows_; ++i) {
        std::cout << "Enter items for row " << i << "." << std::endl;
        for(int j = 0;j < num_cols_; ++j) {
            std::cin >> array_[i][j];
        }
    }

我可以用值填充数组并访问它们,我的复制构造函数和移动赋值操作符是有效的,但是移动赋值操作符抛出了一个奇怪的错误。下面是定义

template<typename Comparable>
Matrix<Comparable>& Matrix<Comparable>::operator= (Matrix<Comparable> && rhs) {
    delete[] array_;
    array_ = new Comparable*[rhs.num_rows_];    
    for(int i = 0;i < rhs.num_rows_;++i) {
        std::swap(array_[i],rhs.array_[i]);
        rhs.array_[i] = nullptr;
    }
    rhs.num_cols_ = 0;
    rhs.num_rows_ = 0;
    rhs.array_ = nullptr;
    return *this;
}

以语句a = std::move(b);为例。如果b的大小与a不同,则矩阵数据因移动而变形。如果b的列数多于a,多余的列数将被截断;如果b的行数少于a,则多余的行数将保留在a中;如果a比b有更多的列或行,那么多余的列将显示内存地址,而内存地址应该什么都没有。这是一个简单的bug吗?我创建数组的方式有问题吗?

"Move assign"并不意味着"小心地将传入的对象修改为某种'空'值",而是意味着"修改传入的对象是可以的"。

这里的Move赋值应该有一个非常简单的实现:只需交换。

template<typename Comparable>
Matrix<Comparable>& Matrix<Comparable>::operator= (Matrix<Comparable> && rhs) {
    using std::swap;
    swap(array_, rhs.array_);
    swap(num_cols_, rhs.num_cols_);
    swap(num_rows_, rhs.num_rows_);
    return *this;
}

不确定为什么在移动赋值操作符中使用new Comparable*move分配的思想是移动资源,而不是制造新的资源。

你的代码可以像这样:

delete[] array_;
array_ = rhs.array_;
rhs.array_ = nullptr;
num_cols_ = rhs.num_cols_;
num_rows_ = rhs.num_rows_;
return *this;
但是,请考虑使用复制-交换习惯用法。这并不总是最有效的选择,但如果你不是大师,这是一个很好的起点。

注意:如果你真的想使用指针到指针来实现你的矩阵,使用vector<vector<Comparable>>代替。所有的工作都为你完成了;你的代码只是在重新发明轮子。

通常用一个连续的分配来表示一个矩阵,而不是为每一行单独分配,这更简单、更有效,所以您可能需要考虑一下这个想法。