Home > Net >  How is the size of a polymorphic object deduced in a delete operation?
How is the size of a polymorphic object deduced in a delete operation?

Time:06-14

I know that there are a lot of similar questions on SO related to what I am about to ask, I've read many of them and still feel a bit vague, so I decided to ask this question.

Given the following code: (which is essentially the same as in this question)

class A {
public:
    int a;
};

class B : public A {
public:
    int b;
};

int main() {
    A* p = new B;
    delete p;
}

I know that deleting p without defining a virtual destructor in A is an undefined behavior specified in the standard and anything could happen thereafter. However, I'm interested in how the size of the polymorphic object on the heap is determined.

I found two sayings on SO which are potentially contradictory: (or I might have misunderstood)

  • We need the virtual destructor to provide the information about the object on the heap for deleting the object through a base class pointer.

    Possible reference.

    In my understanding to such implementation, if we have the virtual destructor, delete p; will first call the destructor of B (and it knows that the first virtual destructor called will correspond to the type of the object that is really on the heap), and the destructor of B knows the size of an object of type B, so the delete operation can extract that information from the first destructor called and use it to free the memory on the heap after the calls of the destructors are done.

    If this is what happens behind the scenes, then delete p; in the above code will not be able to free the heap memory occupied by the int b, because in the above case the first (and the only) destructor called is the destructor of A, and there is no information about int b to be extracted for the delete operation.

  • The heap can "automatically" deduce the size of the polymorphic object.

    Possible references: 1, 2.

    If this is what happens behind the scenes, then delete p; in the above code will be able to free the heap memory occupied by the int b. And in this case delete p; "accidentally" works properly since object of B does not acquire resources from "elsewhere" (e.g. no data members of pointer).

May I ask which one of the two understandings above is correct?

And if the heap can deduce the information about the polymorphic object, may I ask how exactly the heap does that?

CodePudding user response:

There are two logically separate steps involved in delete p;.

  1. The destructor for the object pointed at by p needs to be called. This step requires doing some sort of calculation at runtime to determine what type of object is being pointed at and, therefore, which destructors to call.
  2. The space pointed at by p is reclaimed by calling operator delete, the function that cleans up memory. The memory manager allocates and deallocates memory separately from whatever sorts of objects might be placed there, and so this step does not require any information about what type of object was pointed at by p.

It’s entirely possible that a particular C compiler / runtime might mix these steps together, but I’d wager that most C implementations treat them completely separately and therefore would not do any runtime polymorphism to determine how many bytes to deallocate. The memory manager almost certainly does its own bookkeeping to track the size of the memory block allocated to hold the object.

CodePudding user response:

new implementations store metadata about the memory they allocate so that delete knows how to free the memory correctly. The size of the object is not calculated by delete, because new already knew the size up front.

Calling destructors is handled via normal polymorphic dispatch. That is why calling delete on a base pointer without a virtual destructor is undefined behavior.

Freeing the allocated memory is a separate operation afterwards, and doesn't need to depend on the type of the class that resides in the memory (unless the class overloads operator delete, that is).

CodePudding user response:

I'm not sure either is correct (of a typical implementation). Destroying an object and deallocating memory are two separate processes, and only the later needs to know the size of the object, or more strictly the size of the block of allocated memory, which may be larger than the size of the object. A common way to do this is to store the size of the block of allocated memory in the bytes immediately preceding the pointer itself.

Also consider that the C heap would commonly be implemented by using the C heap API, which has no knowledge of destructors etc.

  • Related