如何改进" ="矩阵的运算符重载?

时间:2018-03-15 13:51:18

标签: c++

我为具有2D数组的类重载了赋值运算符,但是为了进行内存管理和调整大小,我必须首先删除先前的矩阵,然后构造一个新的矩阵,然后才能开始赋值。

Matrix& Matrix::operator = (const Matrix& m1){
    for (int i = 0; i < m_rows; ++i)
        delete[] m_matrix[i];
    delete[] m_matrix;

    m_matrix = new double*[m1.rows()];
    for (int i = 0; i < m1.rows(); ++i)
        m_matrix[i] = new double[m1.cols()]();

    for (int k = 0; k < m1.rows(); ++k)
        for (int j = 0; j < m1.cols(); ++j)
            m_matrix[k][j] = m1.m_matrix[k][j];

    m_rows = m1.rows();
    m_cols = m1.cols();

    return *this;
}

实际上,这部分是我班级的析构函数:

for (int i = 0; i < m_rows; ++i)
    delete[] m_matrix[i];
delete[] m_matrix;

这部分类似于构造函数:

m_matrix = new double*[m1.rows()];
for (int i = 0; i < m_rows; ++i)
    m_matrix[i] = new double[m1.cols()]();

让我烦恼的是我必须复制构造函数&#39;和析构者&#39;赋值函数中的代码(以及其他一些函数!)使其正常工作。有没有更好的方法来写它?

2 个答案:

答案 0 :(得分:3)

理想的改进是Matrix& Matrix::operator=(const Matrix&) = default;

如果切换到使用std::vector进行矩阵存储,则根本不需要实现复制/移动构造函数/赋值和析构函数。

如果您正在进行的是编程练习,请创建自己的动态数组并在矩阵的实现中使用它。

我不能建议观看Better Code: Runtime Polymorphism by Sean Parent,他有效地演示了为什么你应该努力编写不需要复制/移动构造函数/赋值和析构函数的非默认实现的类。

示例:

template<class T>
class Matrix
{
    std::vector<T> storage_;
    unsigned cols_ = 0;

public:
    Matrix(unsigned rows, unsigned cols)
        : storage_(rows * cols)
        , cols_(cols)
    {}

    // Because of the user-defined constructor above
    // the default constructor must be provided.
    // The default implementation is sufficient.
    Matrix() = default;

    unsigned columns() const { return cols_; }
    unsigned rows() const { return storage_.size() / cols_; }

    // Using operator() for indexing because [] can only take one argument.
    T& operator()(unsigned row, unsigned col) { return storage_[row * cols_ + col]; }
    T const& operator()(unsigned row, unsigned col) const { return storage_[row * cols_ + col]; }

    // Canonical swap member function.
    void swap(Matrix& b) {
        using std::swap;
        swap(storage_, b.storage_);
        swap(cols_, b.cols_);
    }

    // Canonical swap function. Friend name injection.
    friend void swap(Matrix& a, Matrix& b) { a.swap(b); }

    // This is what the compiler does for you, 
    // not necessary to declare these at all.
    Matrix(Matrix const&) = default;
    Matrix(Matrix&&) = default;
    Matrix& operator=(Matrix const&) = default;
    Matrix& operator=(Matrix&&) = default;
    ~Matrix() = default;
};

答案 1 :(得分:1)

赋值运算符的规范实现利用现有功能(复制/移动ctor,dtor和swap();请注意,使用非专用std::swap()会很糟糕。它看起来像这样:

T& T::operator= (T val) {
    val.swap(*this);
    return *this;
}

很好地避免重新实现其他现有逻辑。它还优雅地处理自我分配,这是原始代码中的一个问题(它会起作用,但自我分配通常相当罕见;通过检查自我分配来优化它通常会使代码失望)。

参数通过值传递以利用复制省略。

这种方法的主要警告如下。一般来说,我更喜欢规范实现,因为它通常更正确,并且概述的问题通常不是那么相关(例如,当对象刚刚创建时,转移的内存实际上是#34;热&#34;)。

  1. 它不会尝试重复使用已经分配的,可能&#34; hot&#34;记忆。相反,它总是使用新的内存。
  2. 如果保留的数据量很大,则暂时保留可能超出系统限制的副本。首先重用现有内存和/或释放内存都可以解决这个问题。