为什么需要在 c++ 运算符重载中删除内存并分配新内存

Why need to delete memory and allocate new memory in c++ operator overload

提问人:Ahmed Salama 提问时间:6/22/2021 最后编辑:Remy LebeauAhmed Salama 更新时间:6/22/2021 访问量:141

问:

我正在检查赋值运算符实现,但我不明白这一点:

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
    }
}

为什么我不能只更新现有内存中的值?

C++ 运算符重载 dynamic-memory-allocation assignment-operator

评论

0赞 n. m. could be an AI 6/22/2021
您有多少现有内存?也许太少了?也许太多了?
4赞 fabian 6/22/2021
rhs可能大于为当前 Sting 内容分配的数组。此外,您可能希望使用能够存储字符串的最小内存量;当然,您可以引入一个成员,在某些情况下,该成员可能允许您重用旧数组。(使用与此类似的方法)capacitystd::string
0赞 user4581301 6/22/2021
顺便说一句:这里可能不是最佳选择,但复制和交换成语可以将编写即使是最困难的作业操作员变成绝对的小菜一碟。
1赞 HolyBlackCat 6/22/2021
复制和交换将使其异常安全。想想如果失败了会发生什么。new

答:

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

被复制的来源可以与被分配到的不同。MyStringlengthMyString

无法调整数组的大小。要创建不同大小的数组,必须销毁旧数组并将其替换为新数组。这就是第一个代码正在做的事情。

在第二个代码中,仅当新数组较小或相等时,才有意义重用现有数组,而您没有检查,例如:

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 数,例如:capacitycharchar

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赞 Ted Lyngmo 6/22/2021
@MSalters 即使从 1 MIB 变为空字符串,也不会重新分配。g++clang++
0赞 MSalters 6/23/2021
@TedLyngmo:g++与你自己的逻辑无关;这不是.(甚至更准确地称为 libstdc++)MyStringstd::string
0赞 Ted Lyngmo 6/23/2021
@MSalters 没错。我只是说,做一些最常用的标准库所做的事情可能是可以的。至少每个默认值。
0赞 Ahmed Salama 6/23/2021
感谢您的回答,所以,这种方法只是为了确保 LHS 的大小与 RHS 相同,对吧?