移动构造函数

时间:2017-03-22 10:46:47

标签: c++ c++11

我是C ++ 11的新手,所以我仍然坚持其概念。

这是我的问题:

我有一个矩阵类:

class matrix
{

private:

  double** data;
  size_t number_lines;
  size_t number_columns;

  size_t capacity_lines;
  size_t capacity_columns;

public:
....

}

我已经提供了一个复制构造函数,一个移动构造函数......

我已经将乘法运算符*(double x)重载为乘以标量x的矩阵元素并返回乘法矩阵。 这是代码:

matrix matrix::operator*(double lambda)
{
double** aux_data = new double*[number_lines];
for (size_t i = 0; i < number_lines; i++)
{
    aux_data[i] = new double[number_columns];
    for (size_t j = 0; j < number_columns; j++)
        aux_data[i][j] = lambda*data[i][j];
}
return matrix(aux_data, number_lines, number_columns);
}

函数的返回是一个右值引用,因此它调用了移动构造函数。这是移动构造函数的代码:

matrix::matrix(const matrix&& moved_copy)
{
if (this != &moved_copy) 
{
    number_columns = moved_copy.number_columns;
    number_lines = moved_copy.number_lines;
    data = moved_copy.data;
}
}

这个移动构造函数的问题在于它执行浅拷贝而不是深拷贝(就像我猜的每个移动构造函数,否则这个移动构造函数是什么)所以成员数据< / em>指向 moved_copy.data 指向的对象,但此对象是operator * =()函数的本地对象,因此当操作符超出范围时,对象消失了,我有一个悬空指针。所以我的问题是:我应该在移动构造函数中执行深层复制,还是有办法解决这个问题而不这样做?

谢谢。

4 个答案:

答案 0 :(得分:4)

不,你不应该在移动构造函数中制作深层副本。移动构造函数的重点是获取一些复制成本高昂的资源。

在这种情况下,您的data指针的所有权可以从现有的matrix转移到新构造的matrix对象。但我们的想法是所有权转移到新对象,而不是与新对象共享所有权。在这种情况下,这只意味着将moved_copy.data设置为nullptr,这样就不会在data被删除时删除它。

matrix::matrix(matrix&& moved_copy)
{
    number_columns = moved_copy.number_columns;
    number_lines = moved_copy.number_lines;
    data = moved_copy.data;
    moved_copy.data = nullptr;
}

请注意,我还删除了你的if守卫:没有办法从自身构造一个对象,因此移动构造函数并不需要它(虽然它对移动赋值运算符很有用)。

我还从const删除了moved_copy。移动构造函数需要修改移动对象的状态以获取其资源的所有权,因此不能使用const

编辑:从actually possible构建一个对象,但它并不是你真正需要防范的东西。

答案 1 :(得分:0)

此移动构造函数的问题在于data成员在移动后在两个对象上具有相同的值,这样当第一个对象被删除时,第二个对象具有指向已删除内存的指针。

将移动构造函数更改为:

matrix::matrix(matrix&& moved_copy)
{
    if (this != &moved_copy) 
    {
        number_columns = moved_copy.number_columns;
        number_lines = moved_copy.number_lines;
        data = moved_copy.data;
        moved_copy.number_columns = 0;
        moved_copy.number_lines = 0;
        moved_copy.data = nullptr;
    }
}

可以省略检查if (this != &moved_copy),因为对象通常不是通过自身移动来构造的。

答案 2 :(得分:0)

不,您不应该在移动构造函数中执行深层复制。否则你不会获得任何东西,移动构造函数的概念就会被破坏:

matrix::matrix(matrix&& moved_copy)
: data(moved_copy.data),
  number_rows(moved_copy.number_rows),
  number_columns(moved_copy.number_columns),
  capacity_rows(moved_copy.capacity_rows),
  capacity_columns(moved_copy.capacity_columns) {

  moved_copy.data = nullptr;


}

此外,避免将二元运算符定义为成员函数,因为您打破了交换性的数学属性。也就是说,虽然:

matrix M;
...
matrix K = m * 2.0;

会奏效。以下赢了:

matrix M;
...
matrix K = 2.0 * m;

首选将二元运算符定义为自由函数。

matrix operator*(matrix const &m, double lambda) {
  matrix out(m.aux_data, m.number_rows, m.number_columns);

  ...

  return out;
}

matrix operator*(double lambda, matrix const &m) {
  return m * lambda;
}

答案 3 :(得分:0)

  

我是C ++ 11的新手。

所以你不会介意我建议你用std :: vector来实现你的矩阵,因为你的所有移动问题都会解决:

这是一个实施的开始:

#include <vector>
#include <cstddef>
#include <iostream>

class matrix
{

private:

    std::vector<double> storage_;
    std::size_t         row_capacity_;
    std::size_t         rows_;
    std::size_t         cols_;

    std::size_t get_location(std::size_t row, std::size_t col) const
    {
        return row * row_capacity_ + col;
    }

public:
    matrix(std::size_t rows, std::size_t cols, std::size_t row_capacity, std::size_t col_capacity)
        : storage_(row_capacity * col_capacity)
        , row_capacity_(row_capacity)
        , rows_(rows)
        , cols_(cols) {}

    matrix(std::size_t rows, std::size_t cols)
        : matrix(rows, cols, rows, cols) {}

    //
    // note that all move/copy operations are automatically generated.
    // "The rule of none"
    //

    std::size_t get_rows() const { return rows_; }

    std::size_t get_cols() const { return cols_; }


    std::size_t get_capacity_rows() const { return row_capacity_; }

    std::size_t get_capacity_cols() const { return storage_.capacity() / row_capacity_; }

    double& at(std::size_t row, std::size_t col)
    {
        return storage_[get_location(row, col)];
    }

    double const& at(std::size_t row, std::size_t col) const
    {
        return storage_[get_location(row, col)];
    }
};


int main()
{
    auto m = matrix(3, 3);
    m.at(1, 1) = 2;

    std::cout << m.at(1, 1) << std::endl;
    std::cout << m.get_cols() << std::endl;
    std::cout << m.get_rows() << std::endl;
    std::cout << m.get_capacity_cols() << std::endl;
    std::cout << m.get_capacity_rows() << std::endl;
}