使用析构函数和复制构造函数的 C++ 赋值运算符
C++assingment operator using destructor and copying constructor
我正在研究具有多个动态字段的 class,我正在寻找编码赋值运算符的快速方法。
假设我有一些基本的 class Cla
,它存储动态整数数组 (arr
) 和所述数组的大小 (n
)。
我已经编码了:
Cla::Cla(int* arr, int n) : n(n)
{
this->arr = new int[n];
//allocation error handling
while (--n >= 0)
this->arr[n] = arr[n];
}
Cla::~Cla()
{
delete[] arr;
}
Cla::Cla(const Cla& copy) : Cla(copy.arr, copy.n){}
Cla& Cla::operator= (const Cla& asg)
{
this->~Cla();
*this = asg;
return *this;
}
除 operator=
外,其他都正常工作。我的想法是,我将销毁我的对象,然后使用复制构造函数再次创建它(为了示例的简单性,我不考虑两个对象具有相同大小并且不需要重新分配和新分配)。它可以编译,但在执行时会给我一些讨厌的错误。
你能给我一些关于如何更正这段代码的建议吗?它甚至有可能以这种方式工作吗?
(我知道如何编写一个赋值运算符,我只是想问一下是否可以使用析构函数和复制构造函数来完成它。我在互联网上找不到类似的东西。)
您的 operator=
有未定义的行为。首先,您不能在未分配 placement-new
的对象上手动调用析构函数。其次,一旦一个对象被销毁,它就不能再被使用,这意味着一旦 this->~Cla()
被调用,*this = asg
正在访问无效的内存,因为 this
不再指向一个有效的对象。第三,你的 operator=
是 运行 一个无休止的递归循环,一遍又一遍地调用自己,直到调用堆栈爆炸(如果你幸运的话)。
由于您想使用复制构造函数,因此使用 copy-swap idiom 会更好地为您的 operator=
服务。构造一个本地对象以使用您的复制构造函数,然后将该对象的内容与 this
交换,以便 this
获得复制数据的所有权,并且本地对象在旧数据消失时释放旧数据超出范围,例如:
Cla& Cla::operator= (const Cla& asg)
{
if (&asg != this)
{
Cla temp(asg);
std::swap(arr, temp.arr);
std::swap(n, temp.n);
}
return *this;
}
或者:
void Cla::swap(Cla &other)
{
std::swap(arr, other.arr);
std::swap(n, other.n);
}
Cla& Cla::operator= (const Cla& asg)
{
if (&asg != this) {
Cla(asg).swap(*this);
}
return *this;
}
也就是说,您的复制构造函数委托到您的转换构造函数这一事实意味着您使用的是 C++11 或更高版本,在这种情况下,您还应该实现将语义移动到您的 class 中,而不仅仅是复制语义,例如:
Cla::Cla() : arr(nullptr), n(0)
{
}
Cla::Cla(int* arr, int n) : arr(new int[n]), n(n)
{
while (--n >= 0)
this->arr[n] = arr[n];
}
Cla::Cla(Cla &&c) : arr(nullptr), n(0)
{
c.swap(*this);
}
Cla::Cla(const Cla& c) : Cla(c.arr, c.n)
{
}
Cla::~Cla()
{
delete[] arr;
}
void Cla::swap(Cla &other)
{
std::swap(arr, other.arr);
std::swap(n, other.n);
}
Cla& Cla::operator= (Cla asg)
{
asg.swap(*this);
return *this;
}
通过按值传递 asg
参数,operator=
可以根据传递给它的是左值还是右值来决定是在调用站点使用复制语义还是移动语义。编译器将选择合适的构造函数来构造 asg
参数,然后 this
可以获得结果数据的所有权。
我正在研究具有多个动态字段的 class,我正在寻找编码赋值运算符的快速方法。
假设我有一些基本的 class Cla
,它存储动态整数数组 (arr
) 和所述数组的大小 (n
)。
我已经编码了:
Cla::Cla(int* arr, int n) : n(n)
{
this->arr = new int[n];
//allocation error handling
while (--n >= 0)
this->arr[n] = arr[n];
}
Cla::~Cla()
{
delete[] arr;
}
Cla::Cla(const Cla& copy) : Cla(copy.arr, copy.n){}
Cla& Cla::operator= (const Cla& asg)
{
this->~Cla();
*this = asg;
return *this;
}
除 operator=
外,其他都正常工作。我的想法是,我将销毁我的对象,然后使用复制构造函数再次创建它(为了示例的简单性,我不考虑两个对象具有相同大小并且不需要重新分配和新分配)。它可以编译,但在执行时会给我一些讨厌的错误。
你能给我一些关于如何更正这段代码的建议吗?它甚至有可能以这种方式工作吗? (我知道如何编写一个赋值运算符,我只是想问一下是否可以使用析构函数和复制构造函数来完成它。我在互联网上找不到类似的东西。)
您的 operator=
有未定义的行为。首先,您不能在未分配 placement-new
的对象上手动调用析构函数。其次,一旦一个对象被销毁,它就不能再被使用,这意味着一旦 this->~Cla()
被调用,*this = asg
正在访问无效的内存,因为 this
不再指向一个有效的对象。第三,你的 operator=
是 运行 一个无休止的递归循环,一遍又一遍地调用自己,直到调用堆栈爆炸(如果你幸运的话)。
由于您想使用复制构造函数,因此使用 copy-swap idiom 会更好地为您的 operator=
服务。构造一个本地对象以使用您的复制构造函数,然后将该对象的内容与 this
交换,以便 this
获得复制数据的所有权,并且本地对象在旧数据消失时释放旧数据超出范围,例如:
Cla& Cla::operator= (const Cla& asg)
{
if (&asg != this)
{
Cla temp(asg);
std::swap(arr, temp.arr);
std::swap(n, temp.n);
}
return *this;
}
或者:
void Cla::swap(Cla &other)
{
std::swap(arr, other.arr);
std::swap(n, other.n);
}
Cla& Cla::operator= (const Cla& asg)
{
if (&asg != this) {
Cla(asg).swap(*this);
}
return *this;
}
也就是说,您的复制构造函数委托到您的转换构造函数这一事实意味着您使用的是 C++11 或更高版本,在这种情况下,您还应该实现将语义移动到您的 class 中,而不仅仅是复制语义,例如:
Cla::Cla() : arr(nullptr), n(0)
{
}
Cla::Cla(int* arr, int n) : arr(new int[n]), n(n)
{
while (--n >= 0)
this->arr[n] = arr[n];
}
Cla::Cla(Cla &&c) : arr(nullptr), n(0)
{
c.swap(*this);
}
Cla::Cla(const Cla& c) : Cla(c.arr, c.n)
{
}
Cla::~Cla()
{
delete[] arr;
}
void Cla::swap(Cla &other)
{
std::swap(arr, other.arr);
std::swap(n, other.n);
}
Cla& Cla::operator= (Cla asg)
{
asg.swap(*this);
return *this;
}
通过按值传递 asg
参数,operator=
可以根据传递给它的是左值还是右值来决定是在调用站点使用复制语义还是移动语义。编译器将选择合适的构造函数来构造 asg
参数,然后 this
可以获得结果数据的所有权。