【问题标题】:C++ Assignment overload self-assignment issueC++ 赋值重载自赋值问题
【发布时间】:2012-09-08 21:17:47
【问题描述】:

我目前正在开发一个名为 Text 的 ADT,并且正在重载赋值运算符。当我像这样测试运算符时:assignText = alpha 一切输出正常。但是,当我执行assignText = assignText 时,会改为输出两个实心条。

assignText 被声明为 Text 对象。

我的重载赋值运算符如下:

void Text::operator= (const Text& other) { 
bufferSize = other.getLength();
buffer = new char[bufferSize];
buffer = other.buffer; 
}

int bufferSizechar *buffer 在哪里

任何提示或建议将不胜感激。如果还需要什么,请告诉我。

【问题讨论】:

  • 查看复制交换习语。
  • 您的作业可能存在更多问题。
  • 确实如此。 Rule of Zero 在您被允许使用时会发光。

标签: c++ pointers operator-overloading


【解决方案1】:

其他答案已经指出了您的运营商实施的各种问题。在这里,我将尝试了解您发生了什么,即为什么代码的行为与您观察到的一样。如果this == &other,即在自赋值期间,则将当前长度作为新缓冲区的大小。该新缓冲区未初始化,因此此时它可能包含随机字节。在自我分配的情况下,最后一个分配是无操作的。总结一下:

void Text::operator= (const Text& other) { 
  bufferSize = other.getLength(); // Take length from current object
  buffer = new char[bufferSize];  // Create new buffer, old buffer turns into a memory leak
  buffer = other.buffer;          // No-op as both are the same variable
}

所以这告诉你的是你最终得到了一个当前对象大小但内容未定义的缓冲区。在您的情况下,未定义的内容恰好代表您提到的垂直条。

要解决这个问题,请确保根据其他答案和 cmets 的建议修复赋值运算符。

【讨论】:

  • 非常感谢您对我所看到的问题的识别和解释。这是一个非常好的细分,我很感激!
【解决方案2】:

这是内存泄漏。您将两个不同的指针分配给buffer

 buffer = new char[bufferSize];
 buffer = other.buffer; 

【讨论】:

  • 虽然这是真的,但它不是问题的答案。
  • @EdS。对于带有家庭作业标签的问题,这里似乎避免了明确的解决方案。有这方面的政策吗?我不知道随手。 (另一方面,在调试 3 行函数时,您只能做很多提示)
  • 在这种情况下,我倾向于将this meta-answer 作为策略。这证实了您关于不提供即用型完整代码解决方案的观点。不过,对所描述的效果进行解释会很好。
  • 所以通过删除buffer = new char[bufferSize]; 行,我正在删除我创建的内存泄漏。同样以这种方式,当我现在在自分配场景中遇到第三行(即无操作行)时,我将没有指向任何内容的指针(这就是给我两个实心条的输出的原因) .这是一个正确的观察吗?
【解决方案3】:

关于手头的问题,C++ 常见问题解答("Why should I worry about "self assignment"?")对此进行了介绍。首先阅读常见问题解答通常是一个好主意。或者至少略读一下。

当您绝对必须实现复制赋值运算符时,通常复制和交换习语就足够了。它也是异常安全的。是这样的:

void swapWith( MyType& other ) throw()
{
    // swap them members
}

void operator=( MyType other )
{
    swapWith( other );
}

这里拷贝构造函数创建了形式参数拷贝,并且任何异常都发生在那里,所以拷贝构造函数也集中了在拷贝失败的情况下的清理。之后交换两个对象的内容,复制对象的析构函数负责清理该对象的内部内容。 void 结果类型还不是传统的,但我认为浪费代码和时间来支持具有副作用的表达式并不聪明,这是邪恶的。

现在,您可以避免所有这些,只需将std::vector 用作缓冲区即可。

这就是我推荐的,真正简单的解决方案:使用std::vector

【讨论】:

    【解决方案4】:

    这是因为您的赋值运算符根本上是不安全的。您需要在 last 步骤中打破旧状态。考虑

    Text& Text::operator= (const Text& other) {
        auto new_buffer = new char[other.bufferSize];
        std::copy(other.buffer, other.buffer + other.bufferSize, new_buffer);
        // Now consider whether or not you need to deallocate the old buffer.
        // Then set the new buffer.
        buffer = new_buffer;
        bufferSize = other.bufferSize;
        return *this;
    }
    

    这确保了如果遇到任何问题,可以保证Text 对象始终有效——并且作为奖励,它是自分配安全的。所有编写良好的赋值运算符都是自赋值安全的。

    这通常与复制和交换相关,复制然后交换——保证安全。

    【讨论】:

      【解决方案5】:

      这段代码可以完美运行

      Text& Text::operator=(const Text& other) 
      { 
          if(this == &other) /* checks for self assignment */
               return *this;
      
          if(NULL == other.buffer)
               return *this;
      
          if(NULL != buffer) /* assuming buffer=NULL in constructor */
          {
               delete [] buffer;
               buffer = NULL;
          }
      
          bufferSize = other.getLength();
          buffer = new char[bufferSize+1];
          memset(buffer, 0, bufferSize+1);
          memcpy(buffer, other.buffer, bufferSize);
      
          return *this;
      }
      

      试试这个..

      【讨论】:

      • 我理解使用 Objecttype& 和使用 return *this 而不是 void 函数的约定。不幸的是,这是我们要从给定的头文件构造源文件的代码。但我确实认为检查 other.buffer 是否存在是个好主意。
      【解决方案6】:

      经过一番思考和回顾,我想我明白了。鉴于我需要编写一个由头文件指示的 void 函数,我必须从中构造我的实现。我考虑了 Drew Dormann、MvG 以及其他人的 cmets 并提出了这个:

      void Text::operator= (const Text &other) 
      { 
          if (this != &other) // takes care of self-assignment
          { 
               delete [] buffer; // delete the old buffer
               bufferSize = other.bufferSize; // assign size variable
               buffer = new char[bufferSize + 1]; // create new buffer
               strcpy(buffer, other.buffer); // copy array
          }
      }
      

      这行得通,我相信它没有内存泄漏。

      【讨论】:

      • void Text::operator= (const Text & 我们应该从这个函数返回*this,以便它支持多个赋值的链接,如A = B = C; /* A, B, C are instances of class say Text */ 你可以试试我写的下面的代码。此外你应该检查other.buffer是否有效,因为如果它无效,那么它可能会产生分段错误。
      猜你喜欢
      • 2013-12-02
      • 2011-03-06
      • 2019-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-30
      • 2016-08-30
      • 1970-01-01
      相关资源
      最近更新 更多