[英]Is it necessary to block the assignment operator and the copy constructor when using smart pointers?
[英]Using copy constructor and copy assignment operator for pointers to dynamically allocated memory
我正在查看有关“三法则”的解释,并发现以下代码:
// 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;
}
解释事情(在这里: 什么是三人制? )我不确定是否应该在那儿问这个问题(因为它已经发布了一段时间了)还是提出一个新问题,所以我希望我不要不会惹麻烦。 我只想看看我是否正确理解了此代码?
根据我的理解,要复制的对象不是复制指针(因此使两个对象指向同一内存),而是动态分配新的内存,该内存将包含与复制对象相同的数据。包含? 拷贝分配运算符函数的开头有“ delete [] name]”的原因是什么? 是因为使用了=时,指针会被自动复制,因此该新对象指向与复制对象相同的内存,而delete [] name在动态为其分配新的内存以指向它之前删除该内存? 其实,我不确定我要说的是什么,所以有人可以向我解释为什么使用删除吗? 最后,if(this!=&that)部分的目的是什么?
谢谢!
delete
是释放this
对象先前为name
字段拥有的内存。 注意,这是赋值运算符的重载,即:
A x,y;
x = y;
将使用this == &x
和that == &y
调用函数。
这x.name
y的内部成员复制到x,同时确保释放先前分配给x.name
任何动态分配的内存
成员变量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
)会发生什么。 完成这些步骤,您会发现名称将完全丢失。
首先,这个赋值运算符是一个不好的例子 ! 特别是它不是线程安全的! 如果要编写适当的赋值运算符,可以这样编写:
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])
。 delete[] name;
的原因delete[] name;
类不变式是name
指向动态分配的缓冲区,该缓冲区的大小恰好与该人的姓名相同。 注意,我们将创建一个新的缓冲区,以容纳存储在that.name
中的数据副本。 如果我们不delete[]
原始name
所指向的内容,则会泄漏该内存。
至于if (this != &that)
,只要在脑海中追踪如果不存在并且有人在x = x
person x
调用x = x
会发生什么。 首先,我们delete[] name
。 由于this == &that
,这也意味着this->name == that.name
,因此我们也使that.name
指向的缓冲区无效。 在下一步中,我们在(现在已失效)的缓冲区上调用strlen()
,它给出了未定义的行为(例如,可能是段错误)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.