【问题标题】:Why need to delete memory and allocate new memory in c++ operator overload为什么需要在c++运算符重载中删除内存并分配新内存
【发布时间】:2021-09-05 12:46:09
【问题描述】:

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

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

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

【问题讨论】:

  • 你有多少现有内存?也许它太少了?也许是太多了?
  • rhs 可能大于为当前 sting 内容分配的数组。此外,您可能希望使用能够存储字符串的最小内存量;你当然可以介绍一个成员capacity,在某些情况下它可能允许你重用旧数组。 (std::string 使用了类似的方法)
  • 顺便说一句:这里可能不是最佳的,但Copy and Swap Idiom 可以将编写即使是最难的赋值运算符变成绝对的小菜一碟。
  • And 复制和交换将使其异常安全。想想如果new 失败会发生什么。

标签: c++ operator-overloading dynamic-memory-allocation assignment-operator


【解决方案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;
}

【讨论】:

    【解决方案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;
    }
    

    Demo

    【讨论】:

      【解决方案3】:

      对于初学者,operator = 应该返回对分配对象的引用。那就是它应该被声明为

      MyString & operator=(const MyString& rhs);
      

      MyString 类的赋值对象中存储的字符串可以比赋值对象中存储的字符串短。在这种情况下这个语句

      strcpy(this->str, rhs.str);
      

      可能导致内存覆盖超出调用未定义行为的分配字符串。

      注意,不需要调用标准函数strlen,因为数据成员length已经存储了字符串的长度。 运算符可以通过以下方式定义。

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

      【讨论】:

      • @TedLyngmo 不,这将是低效的,因为具有仅包含一个字符的字符串的对象将保留例如几个 Kbytes 的内存。
      • 好的,正在检查。
      • @TedLyngmo 他可以介绍所谓的短字符串优化。:)
      • 是的,SSO 会是一个不错的选择 :-)
      【解决方案4】:

      从中复制的MyString 可以是不同于分配给MyStringlength

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

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

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

      在这种情况下,您应该考虑添加另一个成员 capacity 以更好地区分有多少 chars 物理分配给数组与多少 chars 在数组内部逻辑上有效,例如:

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

      【讨论】:

      • 您必须想知道是否应该为 1 个字符的字符串重用兆字节分配。如果 >100% 或
      • @MSalters 即使​​您从 1 MIB 转到空字符串,g++clang++ 也不会重新分配。 example
      • @TedLyngmo:g++与你自己的逻辑无关;这是MyString 不是std::string。 (甚至更准确地称为 libstdc++)
      • @MSalters 是的。我只是说做一些最常用的标准库做的事情可能没问题。至少默认情况下。
      • 感谢您的回答,所以,这种方法只是为了确保 LHS 的大小与 RHS 的大小相同,对吧?
      猜你喜欢
      • 1970-01-01
      • 2019-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多