[英]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.