簡體   English   中英

在移動構造函數中進行深度復制

[英]Deep copy in a move constructor

我是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);
}

該函數的返回是一個右值引用,因此它調用move構造函數。 這是move構造函數的代碼:

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;
}
}

這個move構造函數的問題在於它執行的是淺拷貝而不是深拷貝(就像我猜的每個move構造函數一樣,否則此move構造函數的意義是什么),因此成員數據指向moved_copy.data指向的對象,但是這個對象是操作符* =()函數的局部對象,因此當操作符超出范圍時,該對象消失了,並且我有一個懸空的指針。 所以我的問題是:我應該在move構造函數中執行深層復制嗎?還是可以解決此問題呢?

謝謝。

不,您不應該在move構造函數中進行深層復制。 move構造函數的全部重點是取得某些復制成本很高的資源的所有權。

在這種情況下,可以將data指針的所有權從現有matrix轉移到新構造的matrix對象。 但是想法是所有權轉移到新對象,而不是與新對象共享所有權。 在這種情況下,這僅意味着將moved_copy.data設置為nullptr ,這樣在銷毀data時它不會刪除您的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防護:無法從自身構造對象,因此move構造函數實際上並不需要(盡管它對move賦值運算符很有用)。

我還從moved_copy刪除了const 移動構造函數需要修改從其移出的對象的狀態,以獲取其資源的所有權,因此不能使用const

編輯: 實際上可以從自身構造一個對象,但這並不是您真正需要提防的東西。

此移動構造函數的問題在於,移動之后, data成員在兩個對象上的值相同,因此當刪除第一個對象時,第二個對象具有指向已刪除內存的指針。

將move構造函數更改為:

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) ,因為對象通常不是通過自身移動來構造的。

不,您不應該在move構造函數中執行深層復制。 否則,您將一無所獲,並且移動構造函數的概念將被破壞:

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;
}

我是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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM