I don't understand one thing. For example, I declare class A and class B which is a child of A:
class A {
public:
int a;
}
class B : public A {
public:
int b;
}
Obviously, if I create instances of A or B, their size in the memory can be determined by the type.
A instanceA; // size of this will probably be the size of int (property a)
B instanceB; // size of this will probably be twice the size of int (properties a and b)
But what if I create dynamic instances and then free them later?
A * instanceAPointer = new A();
A * instanceBPointer = new B();
These are instances of different classes but the program will consider them as instances of class A. That's fine while using them but what about freeing them? To free allocated memory, the program must know the size of memory to free, right?
So if I write
delete instanceAPointer;
delete isntanceBPointer;
How does the program know, how much memory, starting from the address each pointer is pointing to, it should free? Because obviously the objects have different size but the program considers them to be of type A.
Thanks
I am going to assume you know how delete works.
As to how delete
knows how to clean up an inherited instance. That's why you use a virtual
destructor in inheritance context, otherwise you'll have undefined behavior. Basically, the destructor, like every other virtual
function is called via a vtable
.
Also recall that: The C++ compiler implicitly destructs the parent class(es) in your destructor
class A {
public:
int a;
virtual ~A(){}
}
class B : public A {
public:
int b;
~B() { /* The compiler will call ~A() at the very end of this scope */ }
}
That is why this will work;
A* a = new B();
delete a;
By means of vtable
, the destructor ~B()
will be called by delete
. Since the compiler implicitly inserts the destructor call of base class(es) in derived class(es), the destructor of A
will be called in ~B()
.
The behaviour is undefined if you delete an object through a pointer to a base subobject and the class of the subobject does not have a virtual destructor.
On the other hand, if it does have a virtual destructor, then the virtual dispatch mechanism takes care of deallocating the correct amount of memory for the correct address (i.e. that for the complete, most-derived object).
You can discover the address of the most-derived object yourself by applying dynamic_cast<void*>
to any appropriate base subobject pointer. (See also this question.)
To free allocated memory, the program must know the size of memory to free, right?
If you consider the C library malloc
and free
, you'll see that there's no need to specify the amount of memory to be freed when calling free
, even though in that case free
is provided with a void*
so has no way to infer it. Instead, allocation libraries typically either record or can infer enough about the memory provided, such that the pointer alone is sufficient to do the deallocation.
This remains true with the C++ deallocation routines: if a base class provides its own static void operator delete(void*, std::size_t)
and the base-class destructor is virtual
, then it will be passed the size of the dynamic type. By default deallocation ends up at ::operator delete(void*)
which won't be given any size: the allocation routines themselves must know enough to operate.
There are a variety of ways allocation routines may work, including:
storing the size of an allocation
allocating similar-sized objects from a pool of same-sized chunks, such that any pointer into that pool implicitly relates to that chunk size
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With