簡體   English   中英

=運算符是否在C ++中調用構造函數/ new?

[英]Does the = operator call the constructor/new in C++?

假設我有一個(不可變的)矩陣類,它在構造函數中動態創建一個數組,並在解構函數中將其刪除。

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僅僅是垃圾,對不對? 即使我有默認構造函數,它也會被調用嗎? 我將上面的代碼基於此處的示例。 請注意,為簡潔起見,我沒有進行輸入驗證/邊界檢查。

編輯:我想澄清一下,我只關心這樣的電話:

matrix<int> A = B;

其中B已被初始化。

如果您使用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];
    }
}

您現在不再需要擔心內存泄漏,也不需要編寫析構函數,復制構造函數或賦值運算符。

假設我有一個(不可變的)矩陣類,它在構造函數中動態創建一個數組,並在解構函數中將其刪除。

您未實現復制構造函數而違反了規則 (而在C ++ 11中,由於未實現移動構造函數和移動賦值運算符而違反了規則 )。

您的副本分配運算符存在內存泄漏,因為在分配new[] ed數組之前,它不會對舊data數組執行delete[]

嘗試以下方法:

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

但是, 復制和交換的習慣用法會更安全:

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

因為我在operator =函數中沒有默認的構造函數,所以其中的數據只是垃圾,對嗎?

沒有。

即使我有默認構造函數,它也會被調用嗎?

沒有


但是,存在內存泄漏。 您沒有取消分配在構造對象時分配的內存。

您可以初始化一個matrix對象,然后對該對象使用operator= 在這種情況下,此matrix對象中的data將不會成為垃圾,因為它已被初始化。

如果將operator=用於未初始化的matrix實例,例如matrix<int> a = b; ,其中b已被初始化,這意味着您正在調用復制構造函數,該構造函數由編譯器自動生成。 在這兩種情況下,都沒有垃圾值。

暫無
暫無

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

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