【问题标题】:Dealing with memory leaks in class new and delete operators C++处理类 new 和 delete 运算符 C++ 中的内存泄漏
【发布时间】:2012-03-04 01:40:55
【问题描述】:

我很喜欢在 C++ 中使用运算符 newdelete,但经常在稍后在程序代码中调用 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,但据我所知,这很糟糕,会导致内存泄漏。

【问题讨论】:

  • A std::string 可能会有所不同,因此在您的示例中您可以声明 std::string ace; 并且您可能应该了解有关智能指针等的更多信息。en.wikipedia.org/wiki/Smart_pointer
  • 您是否知道智能指针是否适用于 QString(QT) 等非标准 C++ 数据类型?
  • Qt 也有某种智能指针,是的,std::smart_ptr 等。应该与 Qt 一起使用...
  • @user99545:std::string 存在的原因之一是您不必担心newing 和deleteing 字符数组。只要让它成为班级成员,它就会自己照顾自己。
  • ¤ 正如@Insilico 所说,std::string 是一种解决方案。 std::string 让您决定是复制还是共享​​>数据:你不知道它的作用,你也不需要知道(它在功能上就好像它被复制了一样)。要实现自己的复制,您需要阅读“三法则”以进行适当的破坏。要实现自己的共享,请使用std::shared_ptr。不幸的是,标准库中没有复制智能指针,我认为甚至在 Boost 中也没有。干杯&hth.,

标签: c++ memory-management memory-leaks


【解决方案1】:

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

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

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

【讨论】:

  • 感谢您教我 RAII 模式,经过快速研究,我的所有问题都得到了解答。
【解决方案2】:

如果您对aceui->label 都使用std::string,那么一旦foo 对象超出范围,您就不必担心foo->ace 的内存是deleted。

=(赋值操作)上,ui->label 可以使用 Right-Hand 参数的副本。您可以在 C++ std::string reference page for string::operator= 上阅读更多相关信息。

此外,使用智能指针可以完全避免此类问题,例如boost library 提供的智能指针。在 stackoverflow 上阅读 this great post 关于这个主题以获得更好的理解。

【讨论】:

    【解决方案3】:

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

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

    您还想考虑获得通过指针传递给您的对象的所有权是否真的是一个好主意(并分配给在析构函数中有删除的指针成员is a ―不是很明显——传递所有权的方式)。这使对象生命周期管理变得复杂,因为您必须记住是否将某个对象传递给了拥有所有权的对象(如果您有一个严格的总是传递给拥有所有权的对象的策略,这不是问题, 尽管)。像往常一样,智能指针在这里可能会有所帮助;但是您可以考虑复制传递的对象是否是一个更好的选择(对于std::string,它肯定是,但是,无论如何,这里最好有一个直接成员,如上所述)。

    这里有一个完整的规则列表,除非有充分的理由不使用它,否则早期的规则优先于后面的规则:

    1. 不要使用动态分配。
    2. 使用智能指针管理您的动态分配。
    3. new 仅在构造函数中使用,delete 仅在相应的析构函数中使用。
    4. 对于同一类的成员函数中的特定指针,始终使用newdelete。 (其实前面的规则是这个规则的一个特例,但是应该优先于一般规则的一个特例。)

    【讨论】:

      【解决方案4】:

      这是一个更惯用的 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++中消失;)

      【讨论】:

        猜你喜欢
        • 2021-05-08
        • 1970-01-01
        • 2015-11-06
        • 2012-12-07
        • 1970-01-01
        • 1970-01-01
        • 2013-04-20
        • 2010-11-19
        • 2021-10-02
        相关资源
        最近更新 更多