为什么在复制赋值运算符的定义中需要删除?

时间:2016-01-07 10:28:59

标签: c++

我是C ++初学者。我正在使用C ++ Primer(第5版)进行练习。我找到了一个参考 练习13.8来自Github(Here),如下所示。

#include <string>
#include <iostream>

using std::cout;
using std::endl;

class HasPtr {
public:
    HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }
    HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }
    HasPtr& operator=(const HasPtr &hp) {
        std::string *new_ps = new std::string(*hp.ps);
        delete ps;          // I don't know why it is needed here? 
                            // But when I delete this line, it also works.
        ps = new_ps;
        i = hp.i;
        return *this;
    }

    void print() {
        cout << *(this->ps) << endl;
        cout << this->i << endl;
    }

private:
    std::string *ps;
    int i;
};

int main() {
    HasPtr hp1("hello"), hp2("world");
    hp1.print();
    hp1 = hp2;
    cout << "After the assignment:" << endl;
    hp1.print();
}

令我困惑的是HasPtr& operator=(const HasPtr &hp)功能。我不知道为什么这里需要delete ps;。我认为这是一个错误,但在我编译代码时它起作用了。但是,当我删除delete ps;行时,它也有效。所以,我不知道是否需要delete ps;,如果保留它会有什么好处。

4 个答案:

答案 0 :(得分:9)

HasPtr::ps是一个堆分配的std::string指针。

在所有HasPtr构造函数中使用new分配和构造它。因此,当HasPtr::ps被另一个堆分配的指针替换时,必须使用delete释放现有内存以避免内存泄漏。

请注意,在现代C ++中,您应该几乎从不使用newdelete来管理这样的对象。使用智能指针,例如std::unique_ptrstd::shared_ptr,可以安全方便地为您进行内存管理。

我仍然建议您熟悉newdelete,因为大量现有代码会使用它们。 cppreference.com是查找有关该语言的详细信息的好地方。

作为评论中提到的Jan Hudec,将std::string存储在堆上通常非常愚蠢 - std::string是堆分配字符数组的包装器,它已经管理为你记忆。

答案 1 :(得分:3)

需要防止内存泄漏:每个new必须与delete平衡。

在构造函数中为ps分配new的内存。

new_ps分配内存并将其分配给ps时,需要在丢失旧指针值之前释放构造函数分配的内存。

如果省略该行,你的程序确实会“正常工作”,但它会逐渐消耗越来越多的内存,直到最终没有更多的内存。

请注意,您有另一个内存泄漏:您需要创建析构函数,并在其中调用delete ps

正如您所看到的,这变得过于复杂。 更好,抛弃所有这些指针并使用std::string s作为成员变量 - 它会为您处理所有内存管理。

答案 2 :(得分:2)

需要删除以避免内存泄漏。

ps = new_ps;行将ps的地址编辑为指向其他地方。

ps之前指向的内存仍需要释放。删除此行的效果不会立即显示,程序仍然可以正常工作,但是您有内存泄漏。

EG。

ps = address 0 with value 'f'; new_ps = address 1 with value 'g'
Now let ps = new_ps;
ps = address 1 with value 'g'; new_ps = address 1 with value 'g'
So address 0 is no longer something we can access, but it's not been freed either

答案 3 :(得分:0)

在C ++编程中,自动在类对象上调用 delete 调用类的析构函数

在一般实践中,析构函数保存代码以释放资源,即文件指针,删除在类中创建的对象。这样,当您在对象上调用 delete 时,它分配的资源被释放,即返回系统

如果没有释放这些资源,为这些资源分配的内存将不会返回给系统。每次调用对象时,都会丢失一定的内存,并且在一段时间内会丢失一大块记忆。这被称为内存泄漏

因此,当您在对象上调用delete时,如果您在析构函数中执行了这些操作,则确保释放您获取的资源。