0

我正在阅读这篇文章为什么我们需要在 C++ 赋值运算符中删除分配的内存?我对赋值运算符中的新操作分配的内存有疑问。在我们分配给一个 MyString 对象 testObject 之后,它是如何被释放的呢?testObject 的析构函数是否会在超出范围时被调用,或者我必须显式调用 delete 来释放该内存?

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

3 回答 3

3

当你有这个时会发生什么?

{
  MyString s = "Something";
}

这将首先构造 a MyString,它可能会动态分配一个 s 数组char来存储字符串数据。然后s变量超出范围,MyString对象被销毁。它的析构函数应该清理任何动态分配的内存delete[] str

假设您像这样使用它:

{
  MyString s = "Something";
  s = some_other_string;
}

现在MyString对象以相同的方式构造,为字符串数据分配内存。然后第二行将调用赋值运算符。如果按照您所描述的那样实现,则现有分配的char数组将是deleted 并且将分配一个新的数组,其中包含与some_other_string. 然后这个新分配的数组将在s超出范围时被析构函数销毁。

析构函数只是delete[]成员str指向的任何内容。在调用了赋值运算符之后,它正在delete[]处理新分配的数组。

于 2013-04-24T22:17:17.467 回答
0

当您str分配字符串数组来保存rhs. 如果您str在覆盖之前没有删除它,您将永远无法从堆中删除它曾经指向的内容。它曾经指向的那个内存块仍将保留在堆中,并且不可再使用。如果您的程序执行此操作的次数足够多,那么您将耗尽堆中的空间并且您的程序将会死掉。这称为内存泄漏。

于 2013-04-24T22:21:47.283 回答
0

首先,您不必在赋值运算符中删除。如果您覆盖指向以前动态分配的内存的指针,您只需删除。更好的实现方式MyString是跟踪容量,并且仅在需要更多容量时才重新分配(和删除)。

此外,在您的实现中,您在分配之前删除。如果分配失败,这将导致未定义的行为;你必须在删除之前做所有可能失败的事情。在这种情况下,您不需要自我分配测试;自赋值测试的必要性通常表明您的赋值运算符已损坏。

把这两件事放在一起,我们会得到类似的东西:

MyString const&
MyString::operator( MyString const& other )
{
    if ( capacity < other.length ) {
        char* tmp = new char[ other.length ];
        delete str;
        str = tmp;
        capacity = other.length;
    }
    memcpy( str, other.str, other.length );
    length = other.length;
    return *this;
}

删除是有条件的,删除是分配之后,我们总是用length成员来表示长度,而不是混合strlen和长度成员。

于 2013-04-24T23:02:21.387 回答