Why absence of a virtual destructor doesn't cause a memory leak?

Viewed 467

I am playing with the code

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;
}

Some people wrote (why do we need a virtual destructor with dynamic memory?) that it should cause a memory leak but it doesn't. I used g++, then valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt and get

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

I know that some old compilers had problems when they try to free memory in similar situations but it looks like that the modern C++ can free memory seamlessly in this case. So I am curios how is it possible? Maybe delete uses information provided by OS for a given allocated memory block?

EDIT. It is still unclear for me what exactly can cause the UB for delete *pB if we have empty destructors and constructors. An answer for a question (Missing Virtual Destructor Memory Effects) shows that there are no any UB.

3 Answers

I assume you're already aware that delete pB is undefined behaviour. But, why doesn't it leak memory on your machine? That has to do with how your implementation's dynamic memory management works.

Here is one possibility: on some implementations, the default global operator new and operator delete work by calling the C library malloc and free functions. But free needs to be able to do its job when it's just passed a void* with no type or size information, so that implies that malloc must "write down" the size somewhere before it returns. In your program, the call to new B may cause malloc to write down the size of B, so that when free is later passed the pointer, it knows exactly how many bytes to free.

You don't have any memory allocation on A or B, so why a leak should be there? A leak is there when the incorrect destructor is called (due to not being virtual).

If the destructor need not do anything, because there was nothing to clear anyway, no leak would exist.

Change a[20000] to *a and new/delete it, and see what happens.

Btw others say that there is undefined behaviour and there is one, but this question is more related to the fact that the OP does not understand how a destructor works. It's not an exact duplicate of the linked question.

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;
}

Why absence of a virtual destructor doesn't cause a memory leak?

Because behaviour of destroying an object through a pointer to a base class whose destructor is not virtual is undefined. When behaviour is undefined, nothing is guaranteed. For example, there is no guarantee that a memory would be leaked.

That said, it is unclear why would there be an expectation of a memory leak. If you take a look at the destructor of B, you'll notice that it does nothing - the body is empty and the member has trivial destructor. There's no reason to expect that not running a function that does nothing would result in a memory leak.

Related