【问题标题】:Does the = operator call the constructor/new in C++?= 运算符是否在 C++ 中调用构造函数/new?
【发布时间】:2017-07-29 18:00:47
【问题描述】:

假设我有一个(不可变的)矩阵类,它在构造函数中动态创建一个数组,并在解构函数中删除它。

template <typename T>
class matrix {
private:
    T* data;
public:
    size_t const rows, cols;
    matrix(size_t rows, size_t cols) : rows(rows), cols(cols) {
        data = new T[rows*cols];
    }
    ~matrix() {
        delete [] data;
    }
    //access data
    T& operator()(size_t row, size_t col) {
        return data[row*cols + col];
    }
    matrix<T>& operator=(const matrix<T>& other) {
        //what will this->data contain? do I need to delete anything here?
        //should I call the constructor?
        rows = other.rows;
        cols = other.cols;
        data = new T[rows*cols];
        std::copy(&data[0],&data[0] + (sizeof(T)*rows*cols),&other.data[0]);
        return *this;
    }
}

因为我在operator= 函数中没有默认构造函数,所以this 中的数据只是垃圾,对吧?即使我有一个默认构造函数,它会被调用吗?我将上面的代码基于示例here。请注意,为简洁起见,我省略了输入验证/边界检查。

编辑: 我想澄清一下,我只关心这样的电话:

matrix<int> A = B;

其中 B 已经被初始化。

【问题讨论】:

  • 你应该更关心内存泄漏。
  • 您最近的编辑显示了对复制构造函数的调用,not 赋值运算符。所以我之前的评论是——你需要编写一个复制构造函数。
  • 原来的问题是在误认为涉及赋值运算符的情况下提出的。鉴于最新的编辑,它根本不涉及。以“不清楚”结束。
  • 此外,如果您编写了一个复制构造函数(并且它是正确的),那么您链接到的在赋值运算符中使用 std::swap 的示例或多或少会是您的编写您的赋值运算符,即使用对所有成员的简单调用 swap
  • matrix&lt;int&gt; A = B;NOT 一个赋值,它是一个初始化。所以它根本不使用或调用operator=——它调用了复制构造函数。

标签: c++ arrays oop assign assignment-operator


【解决方案1】:

如果你使用std::vector 来存储你的数据,你的类会变得简单很多

template <typename T>
class matrix {
   std::vector<T> data;
public:
    size_t const rows, cols;
    matrix(size_t rows, size_t cols) : rows(rows), cols(cols) {
        data.resize(rows*cols);
    }
    //access data
    T& operator()(size_t row, size_t col) {
        return data[row*cols + col];
    }
}

您现在不必担心内存泄漏,也无需编写析构函数、复制构造函数或赋值运算符。

【讨论】:

  • 改为或调整大小,只是 data(rows * cols) 在初始化列表中。
【解决方案2】:

假设我有一个(不可变的)矩阵类,它在构造函数中动态创建一个数组,并在解构函数中删除它。

您违反了Rule of Three,因为没有实现复制构造函数(C++11 中的Rule of Five,因为没有实现移动构造函数和移动赋值运算符)。

您的复制赋值运算符存在内存泄漏,因为在分配 new[]'ed 数组之前,它不是 delete[]'ing 旧的 data 数组。

试试这个:

template <typename T>
class matrix {
private:
    T* data;
    size_t const rows, cols;

public:
    matrix(size_t rows, size_t cols) : rows(rows), cols(cols) {
        data = new T[rows*cols];
    }

    matrix(const matrix<T> &src) : rows(src.rows), cols(src.cols) {
        data = new T[rows*cols];
        std::copy(data, &data[rows*cols], src.data);
    }

    /* for C++11:

    matrix(matrix<T> &&src) : rows(0), cols(0), data(nullptr) {
        std::swap(rows, src.rows);
        std::swap(cols, src.cols);
        std::swap(data, src.data);
    }
    */

    ~matrix() {
        delete [] data;
    }

    T& operator()(size_t row, size_t col) {
        return data[(row * cols) + col];
    }

    T operator()(size_t row, size_t col) const {
        return data[(row * cols) + col];
    }

    matrix<T>& operator=(const matrix<T>& other) {
        if (&other != this) {
            delete[] data;
            rows = other.rows;
            cols = other.cols;
            data = new T[rows*cols];
            std::copy(data, &data[rows*cols], other.data);
        }
        return *this;
    }

    /* for C++11:

    matrix<T>& operator=(matrix<T> &&other) {
        delete[] data;
        data = nullptr;
        rows = cols = 0;

        std::swap(rows, other.rows);
        std::swap(cols, other.cols);
        std::swap(data, other.data);

        return *this;
    }
    */
};

但是,copy-and-swap idiom 会更安全:

template <typename T>
class matrix {
private:
    T* data;
    size_t const rows, cols;

public:
    matrix(size_t rows, size_t cols) : rows(rows), cols(cols) {
        data = new T[rows*cols];
    }

    matrix(const matrix<T> &src) : rows(src.rows), cols(src.cols) {
        data = new T[rows*cols];
        std::copy(data, &data[rows*cols], src.data);
    }

    /* for C++11:

    matrix(matrix<T> &&src) : rows(0), cols(0), data(nullptr) {
        src.swap(*this);
    }
    */

    ~matrix() {
        delete [] data;
    }

    T& operator()(size_t row, size_t col) {
        return data[(row * cols) + col];
    }

    T operator()(size_t row, size_t col) const {
        return data[(row * cols) + col];
    }

    void swap(matrix<T>& other) noexcept
    {
        std::swap(rows, other.rows);
        std::swap(cols, other.cols);
        std::swap(data, other.data);
    }

    matrix<T>& operator=(const matrix<T>& other) {
        if (&other != this) {
            matrix<T>(other).swap(*this);
        }
        return *this;
    }

    /* for C++11:

    matrix<T>& operator=(matrix<T> &&other) {
        other.swap(*this);
        return *this;
    }
    */
};

在后一种情况下,复制赋值和移动赋值运算符可以在 C++11 中合并为一个运算符:

matrix<T>& operator=(matrix<T> other) {
    other.swap(*this);
    return *this;
}

或者,您可以使用std::vector 遵循零规则,让编译器和 STL 为您完成所有工作:

template <typename T>
class matrix {
private:
    std::vector<T> data;
    size_t const rows, cols;

public:
    matrix(size_t rows, size_t cols) : rows(rows), cols(cols), data(rows*cols) {
    }

    T& operator()(size_t row, size_t col) {
        return data[(row * cols) + col];
    }

    T operator()(size_t row, size_t col) const {
        return data[(row * cols) + col];
    }
};

因为我在operator= 函数中没有默认构造函数,所以这里的数据只是垃圾,对吧?

不能,因为operator= 只能在以前构造的对象上调用,就像任何其他类实例方法一样。

即使我有一个默认构造函数,它会被调用吗?

在你展示的例子中,没有。

我想澄清一下,我只关心这样的电话:

matrix<int> A = B;

该语句根本不调用operator== 的使用只是语法糖,编译器实际上执行复制构造,就好像你已经写了这个:

matrix<int> A(B);

这需要一个您尚未实现的复制构造函数,并且编译器生成的复制构造函数不足以对您的数组进行深层复制。

复制分配看起来更像这样:

matrix<int> A; // <-- default construction
A = B; // <-- copy assignment

matrix<int> A(B); // <-- copy construction
A = C; // <-- copy assignment

【讨论】:

  • 那么在零规则示例中rowscolsdata 由默认的复制构造函数处理?
  • @McAngus: 是的,编译器生成的复制构造函数和复制赋值运算符都适用于 rowscols 成员,以及 data 成员(当它是规则时)-符合三/五/零的类型,例如std::vector
【解决方案3】:

因为我在 operator= 函数中没有默认构造函数,所以这里的数据只是垃圾,对吧?

没有。

即使我有一个默认构造函数,它会被调用吗?

没有


但是,存在内存泄漏。您没有释放在构造对象时分配的内存。

【讨论】:

    【解决方案4】:

    您可以初始化一个matrix 对象,然后对该对象使用operator=。在这种情况下,这个matrix 对象中的data 不会是垃圾,因为它已经被初始化了。

    如果您将operator= 用于matrix 的未初始化实例,例如matrix&lt;int&gt; a = b;,其中b 已经初始化,这意味着您正在调用由编译器自动生成的复制构造函数。在这两种情况下,都没有垃圾值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-22
      • 2021-10-02
      • 2016-01-31
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      相关资源
      最近更新 更多