Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 829
Michal Artazov Avatar asked Jun 06 '16 00:06

Michal Artazov


3 Answers

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

like image 54
WhiZTiM Avatar answered Nov 08 '22 14:11

WhiZTiM


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

like image 28
Kerrek SB Avatar answered Nov 08 '22 14:11

Kerrek SB


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

like image 2
Tony Delroy Avatar answered Nov 08 '22 13:11

Tony Delroy