如何复制或 return 包含动态分配内存的对象?

How to copy or return an object containing dynamically allocated memory?

我有以下 class:

class Matrix {
    int *A;
    int m, n;
...
};
Matrix::Matrix(int rows, int cols){
    m = rows, n = cols;
    A = new int[m*n];
}
Matrix::~Matrix(){
    delete[] A;
}

我正在尝试编写一个 returns 矩阵对象的函数。这样做时,将调用默认的复制构造函数,这意味着每个新返回的对象都指向与函数中的对象相同的动态分配内存块。这会导致程序表现不佳,因为当函数中的对象超出范围时,会释放相同的内存。

我应该如何编写复制构造函数?是否有不需要我逐个元素复制 int 数组的解决方法?

以下是函数,如果有帮助的话:

Matrix Matrix::submatrix(int r, int c){
    if (m <= 1 || n <= 1)
        return Matrix(0, 0);    //return null matrix 

    Matrix T(m-1, n-1);

    int ti = 0; 
    for (int i = 0; i < m; i++){
        if (i == r) continue;
        for (int j = 0; j < n; j++){
            if (j == c) continue;

            T.A[ti] = this->valueAt(i, j);
            ti++;           
        }
    }
    return T;
}

由于您将矩阵有效地存储在一个大数组中,因此实现复制构造函数是微不足道的:

Matrix::Matrix(Matrix const& b) 
    : A(new int[b.m * b.n])
    , m(b.m), n(b.n)
{
    std::copy_n(b.A, b.m * b.n, A);
}

您可能还喜欢将数组存储在 std::unique_ptr<int[]> 中,这样它就不会泄漏内存并防止您意外复制它。

要实现复制分配,我建议使用 swap:

void Matrix::swap(Matrix& b) {
    using std::swap;
    swap(A, b.A);
    swap(m, b.m);
    swap(n, b.n);
}

Matrix& Matrix::operator=(Matrix const& b) {
    Matrix(b).swap(*this); // Re-use the copy constructor.
    return *this;
}

您可能还想以类似的方式实现移动构造函数和赋值:

Matrix::Matrix(Matrix&& b)
    : A(b.A)
    , m(b.m), n(b.n)
{
    b.A = nullptr;
}

Matrix::Matrix& operator=(Matrix&& b) {
    Matrix(std::move(b)).swap(*this); // Re-use the move constructor.
    return *this;
}

或者,将数组存储为 std::vector<int>。这样,编译器生成的复制构造函数、移动构造函数、赋值和析构函数会为您做正确的事情。这也称为 rule-of-zero

对于 class Matrix 您还必须添加复制构造函数和复制赋值运算符,以便以正确的方式使用动态分配的内存。

让我们从复制构造函数开始。基本版本可能如下所示:

Matrix::Matrix(const Matrix& other) {
    A = new int[other.m * other.n];
    std::copy(A, A + other.m * other.n, other.A);
}

你只需要为A分配内存,然后复制内容即可。

现在,复制赋值运算符:

Matrix& Matrix::operator=(const Matrix& other) {
    int* nA = new int[other.m * other.n];
    std::copy(nA, nA + other.m * other.n, other.A);

    delete[] A;
    A = nA;

    return *this;
}

这有点棘手。上面的代码为新矩阵分配内存,将内容复制到新分配的内存中并释放旧内存,然后更新 A。它使用一个临时指针来处理可能抛出的 new.

然后,您还可以添加移动构造函数和移动赋值运算符。

并且,正如所提到的,首选 方法是使用 STL 容器,如 std::vector 来管理它们的内存靠自己。