从堆空间删除对象

Object deletion from heap space

本文关键字:对象 删除 空间      更新时间:2023-10-16

在main函数中,我使用new创建了一个对象,并且没有删除它。下面是一个示例代码,其中类a的对象是类B的成员变量。类B也有一个multimap作为成员变量。

    Class A
    {
    Public:
    A(); //have definition in cpp file
    ~A();//have definition in cpp file
    Private:
    Int a;
    };
    Class B{
    Private:
    Std::multimap<string,string> map_test;
    Public:
    A a;
    B(); //have definition inn cpp file
    ~B();//does not have any definition in cpp file
    };
    int main()
    {
      B *b = new B();
      /* code section where it fills some 1000 key value pairs in the multimap 
         for some purpose */
      return 0;
    }

我的理解:

  1. 即使我没有在这里删除对象,它也不会产生任何问题,因为一旦进程退出,堆空间将被清理。由于我的程序范围如上所述是有限的,没有其他人会重复使用它。那么,不使用delete是好还是坏呢?你对此有什么建议?
  2. 它应该调用对象的默认析构函数,然后调用隐式multimap析构函数。所以不需要显式地清除multimap。如果我说错了,请纠正我。
  3. 在父类中,它只是声明了析构函数,没有任何定义。那么它会调用隐式析构函数还是忽略它呢?(没有理由不定义它,只是要求更好的理解。)
  4. 如果在父类的情况下调用隐式析构函数,它应该调用这里定义的子类析构函数吗?
  5. 当父类对象使用new实例化时,它将在堆中创建。那么这个父对象的成员变量将存储在哪里。例如,对象"a"是一个成员变量,通过查看该成员对象的声明,它似乎是在堆栈中创建的。我只是困惑这里的父对象和它的成员子对象确切的内存创建发生。你能帮我理解一下吗?
  1. 是的,只要你的对象是在main中创建的。然而,如果你想改变这一点,例如创建B的多个实例,或者在另一个类中使用它,等等,等等,那就是另一回事了。此外,像valgrind这样的内存检查工具会在新的w/o删除时给你误报,你会试图忽略它。但是,如果它成为一种习惯,您可能会忽略真正的内存泄漏。

  2. 正确,现在如果它是map<string, string*>,那么你可能需要清理

  3. 它将调用默认析构函数

  4. 我猜,你在问基类成员变量存储在哪里?它们也存储在堆上。

你问了很多问题。

首先,最好的做法是总是清除内存,即使进程退出并清除所有内存(就像它所做的那样)。使用shared_ptr很容易做到。

析构函数总是按照正确的顺序被调用,然而你的multimap是一个危险的操作,你应该清除multimap中的元素,因为如果你存储指针,它会导致严重的泄漏

    大多数操作系统会在进程退出时清理进程的堆空间。如果有一堆嵌入式操作系统没有这种情况,我不会感到惊讶,而且它仍然是不好的做法,因为你确实有内存泄漏。当您泄漏内存时,不会调用析构函数。也就是说,如果您没有实现析构函数,而是依赖于编译器生成的默认析构函数,那么您也不应该声明它——事实上,我很惊讶您没有得到链接器错误。是的,只要你遵守容器对值语义的要求(即没有原始指针),multimap的默认析构函数将删除multimap的内容。
  1. 这是不好的做法,作为程序员,你应该确保你总是正确地管理你的资源。另外,我不认为堆分配b有任何理由,你可以在堆栈上创建对象,而不必考虑资源管理。如果你声明它,你必须提供一个实现,因为析构函数的声明将隐式禁用编译器生成的析构函数。这就是为什么我上面说,我很惊讶你没有得到一个链接错误。
  2. 编译器将通过遍历层次结构并以相反的构造顺序调用析构函数来处理基类的销毁。不,包含A和B的整个对象将在堆上构造——这就是为什么你可以将指向B的指针别名为指向A的指针,并与派生类交互,就好像它是基类一样。

事实:c++中没有原生的(私下的)垃圾收集,尽管有很多东西可以强制某种类型的垃圾收集。

因此,在您的代码中,您有内存泄漏。结束时的范围分配一个B,分配的内存不识字免费

浏览你的问题列表:

  1. 不,如果你不删除指针,内存不会被释放。关于这个话题,你可以做一些事情:

    • 使用RAII习惯用法(资源获取是初始化),即使用对象来管理内存:
    • 使用智能指针:这些结构强制RAII(在我看来最常见的是std::shared_ptrstd::unique_ptr),并确保它们所负责的内存被正确释放。
  2. 视情况而定。如果您使用new操作符分配对象,然后将它们插入到映射中,但是它们没有在其他任何地方被引用,那么您应该手动删除映射的每个条目。在这种情况下它不适用,因为映射类型不是指针。

  3. 类甚至可以省略析构函数声明。如果省略它们,编译器将生成析构函数(还包括复制赋值操作符、复制构造函数和默认构造函数)。

  4. Edited:这取决于,如果你声明了成员A a;,你不必显式地删除它,当声明它为成员的类调用其析构函数时,它的析构函数将被调用。但是如果它是一个你分配的指针(例如在构造函数中),那么你必须在析构函数中删除它。

  5. 一旦你为一个对象使用动态内存分配,整个对象就在堆上,不管它的成员是如何声明的。