我正在查看三规则的解释,并找到以下代码:
// 1. copy constructor
person(const person& that)
{
name = new char[strlen(that.name) + 1];
strcpy(name, that.name);
age = that.age;
}
// 2. copy assignment operator
person& operator=(const person& that)
{
if (this != &that)
{
delete[] name;
// This is a dangerous point in the flow of execution!
// We have temporarily invalidated the class invariants,
// and the next statement might throw an exception,
// leaving the object in an invalid state :(
name = new char[strlen(that.name) + 1];
strcpy(name, that.name);
age = that.age;
}
return *this;
}
解释事物(见What is The Rule of Three?) 我不确定我是否应该在那里提出这个问题(因为它已经发布了一段时间)或提出一个新问题,所以我希望我没有遇到麻烦。我只是想看看我是否正确理解了这段代码?
从我的理解是,不是复制指针(因此有两个对象指向同一个内存),被复制的对象是动态分配新内存,然后包含与对象相同的数据这是复制包含?那么复制赋值运算符函数在开头有“delete [] name]”的原因是什么?是因为当使用=时,指针会自动复制,因此这个新对象指向与复制对象相同的内存,而删除[]名称会在动态分配新内存之前删除该内存以使其指向?实际上,我不确定我在说什么,所以有人可以向我解释为什么它使用删除?最后,if(this!=& that)部分的目的是什么?
谢谢!
答案 0 :(得分:3)
delete
的要点是释放this
字段name
对象先前拥有的内存。请注意,这是赋值运算符重载,即:
A x,y;
x = y;
会使用this == &x
和that == &y
调用该函数。
这会将y的内部成员复制到x,同时确保释放先前分配给x.name
的任何动态分配的内存
答案 1 :(得分:1)
成员变量name
是一个指针。它(几乎总是)指向在堆上分配的char[]
(即new
)。
我们想将另一个对象的名称复制到这个名称:
strcpy(name, that.name);
但是之前我们必须确保name
指向的数组足以容纳新名称(以及终止'\ 0'),因此我们分配空间与new
:
name = new char[strlen(that.name) + 1];
但是 name
之前指向的空间怎么样?我们不想放弃它,那将是内存泄漏,所以在重新分配指针之前我们应该delete
:
delete[] name;
对于this != &that
,考虑如果一些粗心的人使用从一个person
到其自身(Alice = Alice
)的赋值运算符会发生什么。完成这些步骤,您将看到该名称将完全丢失。
答案 2 :(得分:1)
首先,此赋值运算符是错误示例!特别是它不是线程安全的!如果你想编写一个合适的赋值运算符,你可以这样写:
person& operator=(person other) {
other.swap(*this);
return this;
}
其中swap()
是您想要的成员函数,只需交换所有成员:
void swap(person& other) {
std::swap(this->name, other.name);
std::swap(this->age, other.age);
}
关于你的问题:
if (this != &that)
以表明代码错误!基本上有两种情况是错误的:
new
在C ++中分配内存有两种形式:
new T(args)
或new T{args}
分配单个对象,其中T
不是typedef
数组类型,args
是占位符构造函数参数。这样分配的内存需要使用delete ptr
释放,其中ptr
是从上面的表达式返回的结果。通常,内存不是显式释放,而是传递给负责释放内存的对象,例如std::unique_ptr<T>(new T(args))
:std::unique_ptr<T>
的析构函数将根据需要调用delete
。new T[n]
,其中n
是数组的大小。这样分配的对象需要使用delete[] array
释放,其中array
是从数组分配返回的结果。这很少见,因为通常您宁愿使用std::string
或std::vector<T>
来分配数组。您还可以使用上面提到的类模板,但需要指明需要发布数组:std::unique_ptr<T[]>(new T[n])
。答案 3 :(得分:0)
delete[] name;
的原因是类不变量是name
指向动态分配的缓冲区,其大小完全符合人名。请注意,我们即将创建一个新的缓冲区来容纳that.name
中存储的数据副本。如果我们没有delete[]
我们原来的name
指向的内容,我们就会泄漏那个记忆。
对于if (this != &that)
,只需在心理上跟踪如果不存在会发生什么,并且有人在x = x
对象上调用person x
。首先,我们delete[] name
。从this == &that
开始,这也意味着this->name == that.name
,因此我们也使that.name
指向的缓冲区无效。在下一步中,我们在(现在无效的)缓冲区上调用strlen()
,该缓冲区提供未定义的行为(可能是例如段错误)。