处理类 new 和删除运算符中的内存泄漏 C++

Dealing with memory leaks in class new and delete operators C++

本文关键字:内存 泄漏 C++ 运算符 new 删除 处理      更新时间:2023-10-16

我喜欢使用运算符newdelete C++,但后来在程序代码中调用delete时经常遇到问题。

例如,在下面的代码中:

class Foo {
public:
    string *ace;
    Foo();
    ~Foo();
};
Foo::Foo() {
    ace = new string;
}
Foo::~Foo() {
    delete ace;
}
void UI::ButtonPressed() { //Happens when an event is triggered
    Foo *foo = new Foo;
    ui->label = ace; //Set some text on the GUI
    delete foo; //Calls the destructor, deleting "ace" and removing it from the GUI window
}

我可以声明一个new字符串,但是当我delete它时,它会从 GUI 表单中删除该值,因为该字符串现已被删除。

以后有没有办法以某种方式删除这个分配的字符串?

我不想将其声明为全局变量,然后将其delete在程序源代码的最后一行。我永远不能打电话给delete但从我所学到的,这很糟糕,会导致内存泄漏。

您应该阅读有关 RAII 模式的信息。对于C++程序员来说,这是最重要的概念之一。

基本思想是资源(新对象、HTTP 连接等(的生存期与对象的生存期相关联。这是编写异常安全代码所必需的。

在您的情况下,UI 小部件将创建对象的副本,并在其自己的析构函数中释放它。然后,调用代码可以立即释放其副本(在另一个析构函数中(。

如果您同时将std::string用于aceui->label,那么您不必担心一旦foo对象超出范围,foo->ace的内存就会被delete

右手参数的副本可用于= ui->label(赋值操作(。您可以在C++ std::string参考页面上阅读有关它的更多信息,以获取string::operator=

此外,通过使用智能指针(例如 boost 库提供的指针(可以完全避免此类问题。阅读这篇关于这个主题的关于stackoverflow的好文章,以获得更好的理解。

好吧,你的代码有很多话要说。有些事情已经说过了,例如,你应该让字符串成为普通成员,这样分配/去allcoation问题就完全消失了(这是C++程序的一般规则:如果你不是绝对必须使用动态分配,那就不要,句号(。此外,使用适当的智能指针将为您进行内存管理(也是C++中的一般规则:除非确实必须,否则不要自己管理动态分配(。

但是,让我们假设您必须使用动态分配,并且必须在此处使用原始指针和直接newdelete。然后另一个重要的规则进来了(实际上不是C++特定的规则,而是一般的OO规则(:不要公开成员。使其成为私有成员,并提供用于设置它的公共成员功能。然后,该公共成员函数可以在将指针分配给新对象之前正确删除旧对象。请注意,一旦您分配了指针,除非您已将旧值存储在其他位置,否则旧值将永远丢失,如果在此之前尚未删除该对象,则以后无法删除它。

您还需要考虑获取通过指针传递给您的对象的所有权是否真的是一个好主意(并且分配给在析构函数中具有删除的指针成员是一种传递所有权的方式(不是很明显(。这使对象生存期管理复杂化,因为您必须记住是否将某个对象传递给所有权声明对象(但是,如果您有始终传递给所有权声明对象的严格策略,这不是问题(。像往常一样,智能指针可能会在这里有所帮助;但是,您可以考虑复制传递的对象是否是一个更好的选择(std::string肯定是这样,但是,无论如何最好有一个直接成员,如上所述(。

因此,这里有一个完整的规则列表,其中较早的规则优先于较晚的规则,除非有充分的理由不使用它:

  1. 不要使用动态分配。
  2. 使用智能指针管理动态分配。
  3. 仅在构造函数中使用new,仅在相应的析构函数中使用delete
  4. 始终具有同一类的成员函数中特定指针的newdelete。 (实际上,前面的规则是此规则的特例,但是一种特殊情况,应该优先于一般规则。

这是一个更惯用的C++程序:

class Foo {
public:
    std::string ace;
    Foo() : ace() {
      // nothing to do here. ace knows how to create itself…
    }
    // and copy itself…
    Foo(const Foo& other) : ace(other.ace) {}
     // and clean up after itself…
    ~Foo() {
    }
    // and copy/assign itself…
    Foo& operator=(const Foo& other) {
      this->ace = other.ace;
      return *this;
    }
};

void UI::ButtonPressed() {
  // `new` is not needed here, either!
  Foo foo;
  ui->label = foo.ace; //Set some text on the GUI
  // `delete` is not needed here
}

如果你真的需要调用new,请始终使用适当的智能指针 - 写入delete被从现代C++ ;)中驱逐

出去