Why is there no memory leak with non-virtual destructor - c++

I was told that there is no memory leak of B->b in the following code:
struct A {}; // no virtual destructor
struct B : public A {
int b;
}
int main() {
A* a = new B {};
delete a;
}
If it's true, could you explain why?

Because that is how undefined behaviour works. Memory isn't guaranteed to leak. But neither is it guaranteed to not leak. Any behaviour is possible as far as the language is concerned.
why this is Undefined Behaviour
Because a non-virtual destructor is called through a pointer whose dynamic type is of another (derived) type.
whether anything concretely can be said about the lifetime of the B::b
Well, it is a member of B, so it has the same lifetime as any B object. As for the lifetime of the dynamic B object, we cannot really say much due to the UB.

There's nothing in your structure to leak.
Your destructor doesn't delete the object -- it deletes any memory the object itself creates. For instance, if your "int b" were instead something that itself required destruction, you might have a different problem.
But b itself doesn't require destruction.
You could test this after a fashion by doing almost the same, but making a second class that has a constructor and destructor. Add cout statements in each, and instead of "int b" have first a (non-pointer) instance to your new structure and then a pointer version (that you do a new Foo on), and see what happens.

Related

Using a base class as a safe container for pointers

So I was looking at this question Memory Allocation Exception in Constructor where my boss states in his beautiful answer that the destructor will not be called.
Which makes me wonder,
If I were to write
struct XBase
{
int* a;
char* b;
float* c;
XBase() : a(nullptr), b(nullptr), c(nullptr) {}
~XBase()
{
delete[] a; delete[] b; delete[] c;
}
};
and
struct X : XBase
{
X() {
a = new int[100];
b = new char[100];
c = new float[100];
}
}
Then, if the allocation of c fails (with an exception being thrown), then the destructor of XBase would be called, since the base class has been constructed.
And no memory leak?
Am I correct?
You are right; this will work, because:
By the time X constructor body is executed, XBase is already constructed, and its destructor will be called.
Doing delete or delete[] on null pointers is perfectly valid, and does nothing.
So, if the allocation of a, b or c fails, the destructor of XBase will deallocate everything.
But, obviously, this design makes you write much more code that needed, since you can simply use std::vector or std::unique_ptr<T[]>.
Just to have a formal version here to back up the claims,
§ 15.2/2
An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects [...].
If you're concerned about the memory allocation failing, I would put it in a separate method, like a Build or Create. You could then let the destructor handle it if it has been initialized (but definitely check pointer before blindly delete-ing.
Another solution is smart pointers, which are not designed to fit this case necessarily, but do automatically deallocate their contents.

When we're starting to overwrite memory has lifetime of an object has ended yet?

I cannot resolve the following issue by myself:
Suppose we are reusing a memory in a following way:
struct A
{
int a;
A(){ }
~A(){ }
};
struct B : A
{
int b;
B(){ }
~B(){ }
};
A *a = (A*) malloc(sizeof(A) + sizeof(B));
B *b = new (a) B; //Constructor of B is calling
The lifetime of object reffered to by a has ended before the constructor of B is starting to call or it has ended when the constructor of B has finished?
You try to use the placement new operator to initialize b. This operator does not call the destructor of class A first (a), but initializes a new one into the memory pointed to by a. This is problematic (as mentioned in the comment), because the sizeof(B) is greater than sizeof(A) and you allocated only sizeof(A) at the pointer a. So it is undefined behavior.
The lifetime of the object a formally does not end. You get something like:
class A { int a; };
void* p = malloc(sizeof(A));
A* a1 = new (p) A();
A* a2 = new (p) A();
I think, you will get something like double called destructor on the same memory, but that is something compiler-implementation specific. I don't think, that the standard does say anything about this.
As soon as you enter the constructor of B, a is not pointing to an object A any more.
The reason is that even before the first instruction of the constructor of an object the runtime already has done the VMT, base sub-objects and member initialization.
Also if the constructor of B doesn't terminate because of an exception the memory has already been used anyway and another object eventually present at the same memory address doesn't exist any more.
In other words just don't do that... in C++ objects are not just a pile of bytes and they have rights; for example the right of having their destructor called ;-)
The standard from the relevant section (3.8) says,
The lifetime of an object of type T ends when:
- if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
- the storage which the object occupies is reused or released.
I interpret to mean that the life time of the object pointed to by a ends when members of B are initialized. Until that happens, the storage is not reused.
The lifetime of a ends when you delete it and not before - since forcing two different objects to occupy the same space this way is itself undefined behaviour and definitely not recommended your compiler may or may not treat that memory as available for reuse and overwrite b.
If you need to have two objects occupying the same location, e.g.: Message Variants or you are trying to write your own debugger then you should use a union type.
The other reason that you are not recommended to do this sort of thing is that you will create a maintenance nightmare. e.g. later in your code you could have:
b.b = 3
while (a.a > 0) {
b.b--;
}

Virtual destructor and memory deallocation

I'm not quite sure I understand virtual destructors and the concept of allocating space on the heap right. Let's look at the following example:
class Base
{
public:
int a;
};
class Derived : public Base
{
public:
int b;
};
I imagine that if I do something like this
Base *o = new Derived;
that 8 Bytes (or whatever two integers need on the system) are allocated on the heap, which looks then something like this:
... | a | b | ...
Now if I do this:
delete o;
How does 'delete' know, which type o is in reality in order to remove everything from the heap? I'd imagine that it has to assume that it is of type Base and therefore only deletes a from the heap (since it can't be sure whether b belongs to the object o):
... | b | ...
b would then remain on the heap and be unaccessible.
Does the following:
Base *o = new Derived;
delete o;
truly provoke memory leaks and do I need a virtual destructor here? Or does delete know that o is actually of the Derived class, not of the Base class? And if so, how does that work?
Thanks guys. :)
You're making a lot of assumptions about the implementation, which may
or may not hold. In a delete expression, the dynamic type must be the
same as the static type, unless the static type has a virtual
destructor. Otherwise, it is undefined behavior. Period. That's
really all you have to know—I've used with implementations where
it would crash otherwise, at least in certain cases; and I've used
implementations where doing this would corrupt the free space arena, so
that the code would crash sometime later, in a totally unrelated piece
of code. (For the record, VC++ and g++ both fall in the second case, at
least when compiled with the usual options for released code.)
Firstly, the classes you declared in your example have trivial internal structure. From purely practical point of view, in order to destroy object of such classes properly the run-time code does not need to know the actual type of the object being deleted. All it needs to know is the proper size of the memory block to be deallocated. This is actually something that is already achieved by C-style library functions like malloc and free. As you probably know, free implicitly "knows" how much memory to deallocate. Your example above does not involve anything in addition to of that. In other words, your example above is not elaborate enough to truly illustrate anything C++-specific.
However, formally the behavior of your examples is undefined, since virtual destructor is formally required by C++ language for polymorphic deletion regardless of how trivial the internal structure of the class is. So, your "how delete knows..." question simply does not apply. Your code is broken. It does not work.
Secondly, the actual tangible C++-specific effects begin to appear when you begin to require non-trivial destruction for your classes: either by defining an explicit body for the destructor or by adding non-trivial member subobjects to your class. For example, if you add a std::vector member to your derived class, the destructor of the derived class will become responsible for (implicit) destruction of that subobject. And in order for that to work, you will have to declare you destructors virtual. A proper virtual destructor is called through the same mechanism as any other virtual function is called. That's basically the answer to your question: the run-time code does not care about the actual type of the object simply because the ordinary virtual dispatch mechanism will ensure that the proper destructor is called (just like it works with any other virtual function).
Thirdly, another significant effect of virtual destruction appears when you define dedicated operator delete functions for your classes. The language specification requires that the proper operator delete function is selected as if it is looked up from inside the destructor of the class being deleted. And many implementations implement this requirement literally: they actually implicitly call operator delete from inside the class destructor. In order for that mechanism to work properly, the destructor has to be virtual.
Fourthly, a part of your question seems to suggest that you believe that failing to define a virtual destructor will lead to "memory leaks". This a popular, but completely incorrect and totally useless urban legend, perpetuated by low-quality sources. Performing polymorphic deletion on a class that has no virtual destructor leads to undefined behavior and to completely unpredictable devastating consequences, not to some "memory leaks". "Memory leaks" are not the issue in such cases.
There is no problem in the size of the object being deleted - it is known. The problem solved by virtual destructors can be demonstrated as follows:
class Base
{
public:
Base() { x = new char[1]; }
/*virtual*/ ~Base() { delete [] x; }
private:
char* x;
};
class Derived : public Base
{
public:
Derived() { y = new char[1]; }
~Derived() { delete [] y;}
private:
char* y;
};
Then having:
Derived* d = new Derived();
Base* b = new Derived();
delete d; // OK
delete b; // will only call Base::~Base, and not Derived::~Derived
The second delete will not finalize the object properly. If the virtual keyword was uncommented, then the second delete statement will behave as expected, and it will call Derived::~Derived along with Base::~Base.
As pointed out in the comments, to be strict, the second delete yields an undefined behavior, but it's used here only for the sake of making the point about virtual destructors.

Non-virtual trivial destructor + Inheritance

Given that a class and all its subclasses need no more than the default destructor to release their resources if stored in a variable of the exact type (or pointer to the exact type), can a subclass leak memory if referenced by a base class pointer and then deleted by that pointer?
Example:
#include <memory>
class A {
};
class B : public A {
public:
B () : pInt(new int) {}
auto_ptr<int> pInt; // this is what might leak... possibly more will though
};
void will_this_leak () {
A *pA = new B();
delete pA;
}
"Leak memory"? Why are you talking about leaking memory specifically?
The code you posted produces undefined behavior. Anything can happen in this case: memory leaked, hard drive formatted, program crashed, etc.
P.S. I know that there's a popular urban legend out there that performing polymorphic destruction without a virtual destructor "might leak memory". I don't know who invented that nonsense and why they decided to use "leaking memory" as the primary scenario for what might happen. In reality the behavior in this case has absolutely nothing to do with "leaking memory". The behavior is simply undefined.
From the practical point of view, in your particular case it is rather obvious that there's no real chance for the destructor of your auto_ptr to get called, so the memory owned by that auto_ptr will certainly be leaked. But again, this is the least of this code's problems.
It doesn't matter whether they can leak or not. The C++ standard says that deleting a derived class via a base pointer is undefined behavior if the base does not have a virtual destructor. Specifically from 5.3.5/3:
In the first alternative (delete object), if the static type of the
operand is different from its dynamic type, the static type shall be
a base class of the operand’s dynamic type and the
static type shall have a virtual destructor or the behavior is undefined.
So once you have done such a deletion, your program is in an undefined state, and all questions about leaks are moot.
Yes, this will leak. When you delete the A*, it calls ~A(). Since ~A() is not virtual, it won't know that ~B() needs calling too.
Assuming, of course, that B inherits from A. I'm guessing that's a typo in your question -- the code as it stands won't compile :)
The code as of now exhibits undefined-behavior.
If the static type of the operand is
different from the dynamic type, then
the static type becomes the base class
and it's destructor must be virtual. Else the behavior is undefined.
#include <memory>
class A {
public :
virtual ~A() {} // This makes the derived sub-object destruction first
};
class B : public A {
public:
B () : pInt(new int) {}
auto_ptr<int> pInt;
/* There is no need to write any custom destructor
in this case. auto_ptr will effectively handle deallocating
the acquired resources from free store.
*/
};
void will_this_leak () {
A *pA = new B();
delete pA;
}
With the above changes made, there shouldn't be any undefined behavior or memory leaks.

Is the whole object freed with a non-virtual destructor and a Base class pointer?

If a Base class does not have a virtual destructor (in order to avoid the vtable entry for instance) and the Derived class has only basic attributes, does it free all the memory allocated by new, when the pointer of the Base class is deleted? I know the destructor of the Derived class will not be called but I am wondering if the memory allocated by the whole object will be freed?
I assume also that calling delete on a Derived pointer will free the whole memory space.
Also, if it does not free the Derived class part of the memory, how does it work in the same case but with of a virtual destructor in the Base class, to know how much memory to free?
Example:
class Base {
public:
int a;
int b;
Base() {}
~Base() {}
};
class Derived : public Base {
public:
int c;
int d;
Derived() {}
~Derived() {}
};
int main() {
Base *p = new Derived();
delete p; // is memory allocated for Derived freed?
}
It's undefined behavior, so anything can happen. Quote from the standard [expr.delete]:
In the first alternative (delete object), if the static type of the object
to be deleted is different from its
dynamic type, the static type shall be
a base class of the dynamic type of
the object to be deleted and the
static type shall have a virtual
destructor or the behavior is
undefined.
Just don't do that.
I think it's a good idea to understand what actually happens though to understand why the standard has that requirement.
In your simple case, on a typical implementation, the memory will be freed regardless. That is because operator delete will call free(p). Since Base is the first non-virtual base class of Derived, it happens to be located at the beginning of the allocated memory block. And since free must know the size of the allocated block by its own bookkeeping (being a C function it doesn't know anything about sizes of types), it will deallocate the entire block.
However, since Base doesn't have a virtual destructor, delete p is resolved based on the static-type of *p (which is Base). Consequently, as you're seemingly aware, it will not call the destructor of Derived. If Derived had any non-trivial members or base-classes, those would not be destructed either, so any resources that they manage would be leaked.
When the class has a virtual destructor, the work of freeing the memory is delegated to the destructor. The reason is that, although free knows to determine the size of the block, it would still need to have the pointer to the beginning of it. Base can be at an arbitrary offset within Derived in the general case, therefore Derived destructor will be responsible for adjusting the pointer prior to freeing it. This also adds the need for a separate destructor that would destructor the object without actually freeing the memory; e.g. as a subobject of another derived type, or as explicit destructor call. And let's keep delete[] out of the discussion. When you mark your destructor as virtual, the compiler takes care of all this for you.
The bottom line is that the standard doesn't describe all these implementation details, leaving them instead to—um—the implementation. They could formulate some sensible criteria when deleting a non-virtual base would still be ok; but instead they went with the simple clear-cut "you have a virtual destructor then it's ok, otherwise it's not ok" rule.
Technically, the answer is 'unknown'. Deleting a pointer to derived from a pointer to base that has no virtual destructor is undefined behavior.
Practically speaking though, most implementations just fail to properly delete the stuff in the derived object.
Formally speaking, this is undefined behaviour, so you have no guarantees that the memory is freed, or indeed that your program does anything in particular. It may format your hard disk. It may not. In practice, the memory might well be freed in this case -- but you're daft if you rely on it, and you shouldn't do it. It's as simple as that.
Edit: I Was mistaken: Key prose from draft standard, emphasis mine: Section 5.3.5
3 In the first alternative (delete
object), if the static type of the
object to be deleted is different from
its dynamic type, the static type
shall be a base class of the dynamic
type of the object to be deleted and
the static type shall have a virtual
destructor or the behavior is
undefined. In the second alternative
(delete array) if the dynamic type of
the object to be deleted differs from
its static type, the behavior is
undefined.
There is a good chance the memory will be correctly freed. Just because the Standard doesn't assure this doesn't mean it won't work. It's hard to see how it could fail.
In the very particular case that you describe, all of the memory used by the derived class object will be freed when you don't have a virtual destructor. (If using non-virtual destructors always caused derived classes to leak memory, I don't think the language would allow inheriting from classes that used them!)
Remember, the DESTRUCTOR is not what frees the memory; "delete" frees the memory. The destructor is just a function that gets CALLED by delete.
Blah blah blah extensibility blah blah cause yourself trouble in the future blah blah careful with that etc. etc. etc. You know what I mean, obviously.