为什么没有虚拟析构函数不会导致内存泄漏?

时间:2019-06-27 14:05:30

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

我正在玩代码

struct A {
    char a[20000];
    A() { a[0] = 'A'; }
    ~A() {}
};
struct B : A {
    char a[20000];
    B() { a[0] = 'B'; }
    ~B() {}
};
int main() {
    A *pA = new A;
    A *pB = new B;
    delete pA;
    delete pB;
    return 0;
}

有些人写道(why do we need a virtual destructor with dynamic memory?)应该引起内存泄漏,但不是。 我使用了 g ++ ,然后使用了 valgrind --leak-check = full --show-leak-kinds = all --track-origins = yes --verbose --log-file = valgrind- out.txt 并获得

HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 3 allocs, 3 frees, 132,704 bytes allocated
All heap blocks were freed -- no leaks are possible

我知道一些旧的编译器在类似情况下尝试释放内存时会遇到问题,但是看起来现代C ++在这种情况下可以无缝释放内存。所以我很想知道这怎么可能?也许删除使用操作系统提供的信息来分配给定的内存块?

编辑。对于我来说,目前尚不清楚,如果我们有空的析构函数和构造函数,究竟是什么会导致删除* pB 的UB。问题(Missing Virtual Destructor Memory Effects)的答案表明没有任何UB。

3 个答案:

答案 0 :(得分:5)

我认为您已经知道delete pB是不确定的行为。但是,为什么它不泄漏计算机上的内存?这与实现的动态内存管理如何工作有关。

这是一种可能性:在某些实现中,默认全局operator newoperator delete通过调用C库mallocfree函数来工作。但是free必须在没有任何类型或大小信息的void*传递后才能执行其工作,这意味着malloc必须在其之前的某个位置“写下”大小返回。在您的程序中,对new B的调用可能导致malloc记下B的大小,以便以后再传递free指针时,它确切知道有多少个指针可用字节。

答案 1 :(得分:4)

您在A或B上没有任何内存分配,那么为什么应该存在泄漏? 调用不正确的析构函数时会出现泄漏(由于不是虚拟的)。

如果析构函数不需要执行任何操作,因为无论如何都没有要清除的内容,则不会存在泄漏。

a[20000]更改为* a并新建/删除它,然后看看会发生什么。

其他人说存在未定义的行为,并且有一个行为,但是这个问题与OP不了解析构函数的工作原理有关。它不是链接问题的完全重复。

struct A {
    char* a;
    A() { a = new char[20000]; a[0] = 'A'; }
    ~A() { delete[] a;}
};
struct B : A {
    char* b;
    B() : A() { b = new char[20000]; b[0] = 'B'; }
    ~B() { delete[] b;}
};
int main() {
    A *pA = new A;
    A *pB = new B;
    delete pA;
    delete pB;
    return 0;
}

答案 2 :(得分:4)

  

为什么没有虚拟析构函数不会导致内存泄漏?

因为通过指向销毁对象不是虚拟的基类的指针销毁对象的行为是不确定的。如果行为未定义,则无法保证。例如,不能保证内存会泄漏。

也就是说,目前尚不清楚为什么会有内存泄漏的预期。如果看一下B的析构函数,您会注意到它什么都不做-主体为空,并且成员具有琐碎的析构函数。没有理由不运行不执行任何操作的函数会导致内存泄漏。