= 运算符是否在 C++ 中调用 constructor/new?
Does the = operator call the constructor/new in C++?
假设我有一个(不可变的)矩阵 class,它在构造函数中动态创建一个数组,并在析构函数中删除它。
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。请注意,为了简洁起见,我省略了输入 validation/bounds。
编辑:
我想澄清一下,我只关心这样的电话:
matrix<int> A = B;
其中B已经初始化。
Because I don't have a default constructor within the operator= function, the data in this is just garbage, right?
没有
Even if I had a default constructor would it be called?
没有
但是,存在内存泄漏。您没有释放构造对象时分配的内存。
如果您使用 std::vector
来存储您的数据,您的 class 会变得更加简单
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];
}
}
你再也不用担心内存泄漏,也不需要写析构函数、拷贝构造函数或赋值运算符。
您可以初始化一个 matrix
对象,然后为该对象使用 operator=
。在这种情况下,这个 matrix
对象中的 data
不会是垃圾,因为它已经被初始化了。
如果您使用 operator=
作为 matrix
的未初始化实例,例如 matrix<int> a = b;
,其中 b
已经初始化,这意味着您正在调用复制构造函数,由编译器自动生成。在这两种情况下,都没有垃圾值。
Say I have a (immutable) matrix class that dynamically creates an array in the constructor, and deletes it in the deconstructor.
您没有实现移动构造函数和移动赋值运算符,这违反了 C++11 中的 Rule of Three by not implementing a copy constructor (and the Rule of Five。
您的复制赋值运算符存在内存泄漏,因为在分配 new[]
数组之前,它没有对旧的 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;
}
*/
};
然而,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];
}
};
Because I don't have a default constructor within the operator=
function, the data in this is just garbage, right?
不,因为 operator=
只能在先前构造的对象上调用,就像任何其他 class 实例方法一样。
Even if I had a default constructor would it be called?
在你展示的例子中,没有。
I would like to clarify that I'm only concerned for a call like this:
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
假设我有一个(不可变的)矩阵 class,它在构造函数中动态创建一个数组,并在析构函数中删除它。
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。请注意,为了简洁起见,我省略了输入 validation/bounds。
编辑: 我想澄清一下,我只关心这样的电话:
matrix<int> A = B;
其中B已经初始化。
Because I don't have a default constructor within the operator= function, the data in this is just garbage, right?
没有
Even if I had a default constructor would it be called?
没有
但是,存在内存泄漏。您没有释放构造对象时分配的内存。
如果您使用 std::vector
来存储您的数据,您的 class 会变得更加简单
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];
}
}
你再也不用担心内存泄漏,也不需要写析构函数、拷贝构造函数或赋值运算符。
您可以初始化一个 matrix
对象,然后为该对象使用 operator=
。在这种情况下,这个 matrix
对象中的 data
不会是垃圾,因为它已经被初始化了。
如果您使用 operator=
作为 matrix
的未初始化实例,例如 matrix<int> a = b;
,其中 b
已经初始化,这意味着您正在调用复制构造函数,由编译器自动生成。在这两种情况下,都没有垃圾值。
Say I have a (immutable) matrix class that dynamically creates an array in the constructor, and deletes it in the deconstructor.
您没有实现移动构造函数和移动赋值运算符,这违反了 C++11 中的 Rule of Three by not implementing a copy constructor (and the Rule of Five。
您的复制赋值运算符存在内存泄漏,因为在分配 new[]
数组之前,它没有对旧的 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;
}
*/
};
然而,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];
}
};
Because I don't have a default constructor within the
operator=
function, the data in this is just garbage, right?
不,因为 operator=
只能在先前构造的对象上调用,就像任何其他 class 实例方法一样。
Even if I had a default constructor would it be called?
在你展示的例子中,没有。
I would like to clarify that I'm only concerned for a call like this:
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