Michal Artazov
Michal Artazov

Reputation: 4648

C++ dynamic objects. How is object size determined during runtime?

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

Upvotes: 5

Views: 1129

Answers (3)

WhiZTiM
WhiZTiM

Reputation: 21576

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().

Upvotes: 7

Tony Delroy
Tony Delroy

Reputation: 106126

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

Upvotes: 2

Kerrek SB
Kerrek SB

Reputation: 477160

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.)

Upvotes: 3

Related Questions