提问人:Ahmed Salama 提问时间:6/22/2021 最后编辑:Remy LebeauAhmed Salama 更新时间:6/22/2021 访问量:141
为什么需要在 c++ 运算符重载中删除内存并分配新内存
Why need to delete memory and allocate new memory in c++ operator overload
问:
我正在检查赋值运算符实现,但我不明白这一点:
const MyString& operator=(const MyString& rhs)
{
if (this != &rhs) {
delete[] this->str; // Why is this required?
this->str = new char[strlen(rhs.str) + 1]; // allocate new memory
strcpy(this->str, rhs.str); // copy characters
this->length = rhs.length; // copy length
}
return *this; // return self-reference so cascaded assignment works
}
为什么我不能这样做,而不释放内存然后分配新内存?
void operator=(const MyString& rhs)
{
if (this != &rhs) {
strcpy(this->str, rhs.str); // copy characters
this->length = rhs.length; // copy length
}
}
为什么我不能只更新现有内存中的值?
答:
0赞
R Sahu
6/22/2021
#1
为什么只是我无法更新现有内存中的值
如果 LHS 有足够的内存,则可以这样做。否则,您必须解除分配旧内存并分配新内存。
const MyString& operator=(const MyString& rhs)
{
if (this != &rhs) {
if ( this->length < rhs.length )
{
this->length = rhs.length;
delete[] this->str;
this->str = new char[strlen(this->length) + 1];
}
strcpy(this->str, rhs.str);
}
return *this;
}
0赞
Ted Lyngmo
6/22/2021
#2
仅当字符串长度超过已分配空间时,才需要分配新内存:rhs
MyString& operator=(const MyString& rhs) { // don't return a `const&`
if (this != &rhs) {
if(length < rhs.length) {
delete[] str;
str = new char[rhs.length + 1];
}
length = rhs.length;
std::memcpy(str, rhs.str, length + 1);
}
return *this;
}
请注意,如果不添加成员变量,这将丢失有关额外空间的信息。添加后,它看起来像这样:capacity
MyString& operator=(const MyString& rhs) {
if (this != &rhs) {
if(capacity < rhs.length) {
delete[] str;
capacity = rhs.length;
str = new char[capacity + 1];
}
length = rhs.length;
std::memcpy(str, rhs.str, length + 1);
}
return *this;
}
0赞
Vlad from Moscow
6/22/2021
#3
对于初学者来说,应该返回对分配对象的引用。也就是说,它应该像这样声明operator =
MyString & operator=(const MyString& rhs);
存储在类 MyString 的赋值对象中的字符串可以短于赋值对象中存储的字符串。在本例中,此语句
strcpy(this->str, rhs.str);
可能导致内存覆盖超出调用未定义 Bejavior 的已分配字符串。
请注意,没有必要调用标准函数,因为数据成员长度已经存储了字符串的长度。
运算符可以按以下方式定义。strlen
const MyString& operator=(const MyString& rhs)
{
if ( this != &rhs )
{
if ( this->length != rhs.length )
{
delete[] this->str; // Why is this required?
this->str = new char[rhs.length + 1];
this->length = rhs.length;
}
strcpy( this->str, rhs.str );
}
return *this;
}
评论
0赞
Vlad from Moscow
6/22/2021
@TedLyngmo 不,这将是低效的,因为字符串仅包含一个字符的对象将保留内存,例如几 KB。
0赞
Ted Lyngmo
6/22/2021
好的,只是检查。
0赞
Vlad from Moscow
6/22/2021
@TedLyngmo他可以引入所谓的短字符串优化:)
0赞
Ted Lyngmo
6/22/2021
是的,SSO会是一个不错的选择:-)
2赞
Remy Lebeau
6/22/2021
#4
被复制的来源可以与被分配到的不同。MyString
length
MyString
无法调整数组的大小。要创建不同大小的数组,必须销毁旧数组并将其替换为新数组。这就是第一个代码正在做的事情。
在第二个代码中,仅当新数组较小或相等时,才有意义重用现有数组,而您没有检查,例如:
const MyString& operator=(const MyString& rhs)
{
if (this != &rhs) {
if (rhs.length > this->length) {
delete[] this->str;
this->str = new char[rhs.length + 1];
}
strcpy(this->str, rhs.str);
this->length = rhs.length;
}
return *this;
}
在这种情况下,您应该考虑添加另一个成员,以更好地区分为数组物理分配的 s 数与数组内部逻辑上有效的 s 数,例如:capacity
char
char
MyString()
: str(NULL), length(0), capacity(0)
{
}
MyString(const MyString& src)
: str(NULL), length(0), capacity(0)
{
if (src.str) {
this->capacity = rhs.length; // optionally round up to an even boundary of your choosing
this->str = new char[this->capacity + 1];
strcpy(this->str, src.str);
this->length = rhs.length;
}
}
const MyString& operator=(const MyString& rhs)
{
if (this != &rhs) {
if (rhs.length > this->capacity) {
delete[] this->str;
this->capacity = rhs.length; // optionally round up to an even boundary of your choosing
this->str = new char[this->capacity + 1];
}
strcpy(this->str, rhs.str);
this->length = rhs.length;
}
return *this;
}
评论
0赞
MSalters
6/22/2021
你不得不怀疑你是否应该对一个 1 个字符的字符串重复使用兆字节分配。如果>100%或<50%,也许您应该重新分配。
0赞
MSalters
6/23/2021
@TedLyngmo:g++与你自己的逻辑无关;这不是.(甚至更准确地称为 libstdc++)MyString
std::string
0赞
Ted Lyngmo
6/23/2021
@MSalters 没错。我只是说,做一些最常用的标准库所做的事情可能是可以的。至少每个默认值。
0赞
Ahmed Salama
6/23/2021
感谢您的回答,所以,这种方法只是为了确保 LHS 的大小与 RHS 相同,对吧?
评论
rhs
可能大于为当前 Sting 内容分配的数组。此外,您可能希望使用能够存储字符串的最小内存量;当然,您可以引入一个成员,在某些情况下,该成员可能允许您重用旧数组。(使用与此类似的方法)capacity
std::string
new