在没有虚拟析构函数的情况下删除基类指针会导致内存泄漏吗?

Posted

技术标签:

【中文标题】在没有虚拟析构函数的情况下删除基类指针会导致内存泄漏吗?【英文标题】:Would it lead to memory leak when delete base class pointer without virtual destructor? 【发布时间】:2017-08-08 06:36:49 【问题描述】:

这里是一个解释虚拟析构函数的例子。(见http://www.geeksforgeeks.org/g-fact-37/) 我根据那个例子修改了代码,有一个关于内存泄漏的问题。

假设 Base 类有一个 int num 变量,Derived 类有一个 float money 变量。

在调用delete base_ptr;时,由于基类的析构函数是虚函数,所以应该先调用~derived(),再调用~Base()

我的问题是“函数 delete 是否足够聪明,以至于它可以释放分配给 int num(Base Class) 和 float money(Derived Class) 的内存?

我认为 base_ptr 是 Base* 类型的指针,因此它可能只会释放 Base 类所需的内存量。但是,即使 base_ptr 指向 Base 类的类型,似乎 int 和 float 也会被释放。如果是这样的话,如果我们将~Base()设为非虚拟析构函数会导致内存泄漏吗?使用~Base() 的非虚拟析构函数,我们将错过~Derived() 的调用。因为在基类和派生类“内”没有动态分配任何内容,看来~Derived() 实际上根本没有释放任何内存,delete 的函数将释放int numfloat money 的内存。

#include <iostream>
using namespace std;

class Base 
public:
    int num;

 Base(int n):num(n)
    cout<<"Base::Constructor\n";
 
    virtual ~Base()
    cout<<"Base::Destructor\n";
 
;

class Derived : public Base 
private:
  float money;
public:
 Derived(int n, float m):Base(n),money(m)
    cout<<"Derived::Constructor\n";
 
 ~Derived()
    cout<<"Derived::destructor\n";
 
;



int main() 
    Base *base_ptr = new Derived(1,200.0);
    delete base_ptr;
    return 0;

【问题讨论】:

这段代码很好,但如果 Base 析构函数不是虚拟的,那将是未定义的行为 首先,如果没有虚拟析构函数,代码将具有未定义的行为,并且没有必要讨论隐形独角兽的颜色。其次,在询问程序是否会“只释放一部分内存”之前,请考虑您如何使用mallocfree,并问自己如何告诉free 您想要释放多少内存. C++ 标准明确指出,通过基类指针删除派生类实例是未定义的行为。试图弄清楚什么时候可以“愚弄系统”似乎不值得。 【参考方案1】:

您所描述的是未定义的行为,这意味着任何令人讨厌的东西都可能出错,而不仅仅是内存泄漏。

但实际上,如果继承不是虚拟的,派生类没有其他基类,派生类没有具有非平凡析构函数的成员,您可能会调用 Base::~Base() 析构函数,然后 @ 987654322@ 在指针上调用。 operator delete(void*) 函数只接受一个指针并释放它所指向的所有内存,因此不会出现“内存泄漏”。

【讨论】:

1.所以'delete'总是会释放'new'最初分配的内存,即使它稍后转换为另一种类型?例如,int* ptr = new int(5);删除 (char*)ptr;是否仍会释放分配给 int 而不仅仅是 char 的内存? 2.我认为执行顺序是(使用虚拟析构函数)1)~Derived();它会使可变浮动资金成为“未定义”,但实际上并没有“释放”为浮动资金分配的内存。 2)~基();它会使变量 int num “未定义”,但仍然不会释放任何内存。 3)delete 最终会为基类和派生类释放内存,这些内存最初是由 new 分配的。这样对吗?我认为这是我需要了解的 2 个微妙点。 @jaedong - 你太努力了。 :-) 尽管名称相似,但语言定义的delete 运算符和库中定义的operator delete(void*) 是两个不同的东西。在delete ptr 中,您需要从new 返回指针,并具有正确的类型。否则我们怎么知道要调用哪个析构函数?当在第 3 部分中调用 operator delete(void*) 时,内存是无类型的,就像 free 一样,运行时无论如何都会神奇地知道大小(以几种可能的方式之一,C++ 标准中没有一种描述)。

以上是关于在没有虚拟析构函数的情况下删除基类指针会导致内存泄漏吗?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 虚拟析构函数 (virtual destructor)

删除指向子类的指针会调用基类析构函数吗?

为多态基类声明一个虚析构函数

析构函数 声明为protected

虚析构函数

具有虚拟析构函数的基类子类中的默认析构函数