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 ofB
(and it knows that the firstvirtual
destructor called will correspond to the type of the object that is really on the heap), and the destructor ofB
knows the size of an object of typeB
, 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 theint b
, because in the above case the first (and the only) destructor called is the destructor ofA
, and there is no information aboutint b
to be extracted for the delete operation.The heap can "automatically" deduce the size of the polymorphic object.
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 theint b
. And in this casedelete p;
"accidentally" works properly since object ofB
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;
.
- 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. - The space pointed at by
p
is reclaimed by callingoperator 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 byp
.
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.