Why is it undefined behavior to delete[] an array of derived objects via a base pointer?
Base* p = new Base[n]
creates an n
-sized array of Base
elements, of which p
then points to the first element. Base* p = new Derived[n]
however, creates an n
-sized array of Derived
elements. p
then points to the Base
subobject of the first element. p
does not however refer to the first element of the array, which is what a valid delete[] p
expression requires.
Of course it would be possible to mandate (and then implement) that delete [] p
Does The Right Thing™ in this case. But what would it take? An implementation would have to take care to somehow retrieve the element type of the array, and then morally dynamic_cast
p
to this type. Then it's a matter of doing a plain delete[]
like we already do.
The problem with that is that this would be needed every time an array of polymorphic element type, regardless of whether the polymorphism is used on not. In my opinion, this doesn't fit with the C++ philosophy of not paying for what you don't use. But worse: a polymorphic-enabled delete[] p
is simply useless because p
is almost useless in your question. p
is a pointer to a subobject of an element and no more; it's otherwise completely unrelated to the array. You certainly can't do p[i]
(for i > 0
) with it. So it's not unreasonable that delete[] p
doesn't work.
To sum up:
arrays already have plenty of legitimate uses. By not allowing arrays to behave polymorphically (either as a whole or only for
delete[]
) this means that arrays with a polymorphic element type are not penalized for those legitimate uses, which is in line with the philosophy of C++.if on the other hand an array with polymorphic behaviour is needed, it's possible to implement one in terms of what we have already.
Segmentation fault after delete[] on base class pointer
Simply put, you have undefined behavior. You aren't providing delete[]
with a pointer you got from new[]
. You may think you do, but for the pointers to be the same in the array version, their static type has to match. You converted the pointer into a pointer to a base class.
Practically, when you don't have that added float
, your implementation probably maintains sizeof(B) == sizeof(A)
. So the destructor and deallocation function invocations don't do anything immediately harmful. But it's just as undefined.
How is memory deallocated by delete operator with pointer to base class
Your code has undefined behavior.
It's ok to use
base* ptr = new derived();
delete ptr;
but it is not ok to use
base* ptr = new derived[10];
delete [] ptr;
Here's the relevant text from the C++11 Standard (emphasis mine):
In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
Base pointer to array of derived objects
If you look at the expression p[1]
, p
is a Base*
(Base
is a completely-defined type) and 1
is an int
, so according to ISO/IEC 14882:2003 5.2.1 [expr.sub] this expression is valid and identical to *((p)+(1))
.
From 5.7 [expr.add] / 5, when an integer is added to a pointer, the result is only well defined when the pointer points to an element of an array object and the result of the pointer arithmetic also points the an element of that array object or one past the end of the array. p
, however, does not point to an element of an array object, it points at the base class sub-object of a Derived
object. It is the Derived
object that is an array member, not the Base
sub-object.
Note that under 5.7 / 4, for the purposes of the addition operator, the Base
sub-object can be treated as an array of size one, so technically you can form the address p + 1
, but as a "one past the last element" pointer, it doesn't point at a Base
object and attempting to read from or write to it will cause undefined behavior.
Is it undefined behavior to deallocate a pointer returned by a global replacement operator new, without calling a replacement operator delete? (C++17)
Both examples are undefined behavior. Now that I've taken the time to look through the C++17 standard final draft, I've found the evidence I need.
Example A
With regards to operator new
:
Allocation functions - § 6.7.4.1.2
If the request succeeds, the value returned shall be a
non-null pointer value (7.11) p0 different from any previously returned value p1, unless that value p1 was
subsequently passed to anoperator delete
In example A we call a new-expression, Simple* a = new Simple()
, which internally will call the appropriate operator new
. We bypass operator delete
when we call UserImplementedFree(static_cast<void*>(a))
. Even though operator delete
would invoke this function, and presumably do the same deallocation, the catch is that any subsequent calls to operator new
can now potentially return a pointer that matches the address that a
had. And a
was never passed to operator delete
. So we've broken the rule stated above.
Example B
delete-expression - § 8.3.5.2
...the value of the operand of delete may be a null pointer
value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (4.5)
representing a base class of such an object (Clause 13). If not, the behavior is undefined. In the second
alternative (delete array), the value of the operand of delete may be a null pointer value or a pointer
value that resulted from a previous array new-expression.
83 If not, the behavior is undefined.
In example B, we do not allocate addr
through a new-expression. And then we attempt to use a delete-expression to deallocate it. Which violates the rule above.
What would defined behavior look like?
The main feature of these examples is the separation of construction from allocation, and separation of destruction from deallocation. The standard states the following:
new-expression - § 8.3.4.11
For arrays of
char
,unsigned char
,
andstd::byte
, the difference between the result of the new-expression and the address returned by the
allocation function shall be an integral multiple of the strictest fundamental alignment requirement (6.11) of
any object type whose size is no greater than the size of the array being created. [ Note: Because allocation
functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with
fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating
character arrays into which objects of other types will later be placed. — end note ]
So defined behavior could potentially look like this:
{
//Allocates bytes
char* bytes = new char[sizeof(Simple)];
//Invokes constructor
Simple* a = new ((void *)bytes) Simple();
//Invokes destructor
a->~Simple();
//Deallocates
delete[] bytes;
}
Once again, not necessarily good practice, but defined behaviour.
Possible bug on delete[] for all g++ versions or not defined behaviour for this?
I 'm doing something wrong? or its a g++ bug?
The behavior of your program is undefined:
If the static type of the object that is being deleted differs from
its dynamic type (such as when deleting a polymorphic object through a
pointer to base), and if the destructor in the static type is virtual,
the single object form of delete begins lookup of the deallocation
function's name starting from the point of definition of the final
overrider of its virtual destructor. Regardless of which deallocation
function would be executed at run time, the statically visible version
of operator delete must be accessible in order to compile. In other
cases, when deleting an array through a pointer to base, or when
deleting through pointer to base with non-virtual destructor, the
behavior is undefined.
Why does polymorphism not apply on arrays in C++?
You get undefined behavior because operator delete[]
has no idea what kind of object is stored in the array, so it trusts the static type to decide on offsets of individual objects. The standard says the following:
In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
The array's static type needs to match the element type that you use for the allocation:
Derived* p = new Derived[4]; // Obviously, this works
This Q&A goes into more details on the reason why the standard has this requirement.
As far as fixing this behavior, you need to create an array of pointers, regular or smart (preferably smart to simplify memory management).
Deleting a derived object via a pointer to its base class
It won't leak the object you are deleting, its memory block will be freed.
If you have not declared the destructor in base_class
to be virtual then it will leak any dynamically allocated objects contained within derived_class
that rely on the destructor of derived_class
being called to free them. This is because if the destructor is not virtual, the derived_class
destructor is not called in this case. It also means that destructors of "embedded objects" within derived_class
will not automatically be called, a seperate but additional problem, which can lead to further leaks and the non-execution of vital cleanup code.
In short, declare the destructor in base_class
to be virtual and you can safely use the technique you have presented.
For a coded example, see:
In what kind of situation, c++ destructor will not be called?
Is it undefined behaviour to delete a null void* pointer?
I wonder how you can reach up a situation where you are deleting a pointer only if it is null. But staying in language lawyering mode...
In C++ 03
5.3.5/1
the operand of delete shall have a pointer type or a class type having a single conversion to a pointer type.
void* is a pointer type so a null void pointer meets the static requirement.
5.3.5/2
In either alternative [
delete
anddelete[]
], if the value of the operand of delete is the null pointer the operation has no effect.
And this gives the wanted behavior.
5.3.5/3
In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand's dynamic type and the static type shall have a virtual destructor or the behavior is undefined.
This is not relevant, a null pointer doesn't reference an object on which to check the additional constraint.
In C++ 0X
5.3.5/1
The operand shall have a pointer to object type, or a class type having a single non-explicit conversion function (12.3.2) to a pointer to object type.
void* isn't a pointer to object type, so should be rejected.
What happens when delete a polymorphic object without a virtual destructor?
What happens when b is deleted without a virtual destructor?
We don't know. The behavior is undefined. For most actual cases the destructor of Derived
might no be invoked, but nothing is guaranteed.
5.3.5 Delete
[expr.delete]
(emphasis mine)
In the first alternative (delete object), if the static type of the
object to be deleted is different from its dynamic type, the static
type shall be a base class of the dynamic type of the object to be
deleted and the static type shall have a virtual destructor or the
behavior is undefined.
Related Topics
C++ Array Assignment of Multiple Values
How to Set a Breakpoint in Gdb Where the Function Returns
Why Is My Helloworld Function Not Declared in This Scope
How to Use Standard Library (Stl) Classes in My Dll Interface or Abi
Performance of Qsort VS Std::Sort
How to Remove Constness of Const_Iterator
Measure Execution Time in C++ Openmp Code
Using Sizeof on Arrays Passed as Parameters
Can C++ Code Be Valid in Both C++03 and C++11 But Do Different Things
Linking a Shared Library with Another Shared Lib in Linux
How to Get the Hmodule for the Currently Executing Code
Is It a Good Idea to Return " Const Char * " from a Function
What Is the Printf Format Specifier for Bool
Is Using Double Faster Than Float
Why Can't Static_Cast Be Used to Down-Cast When Virtual Inheritance Is Involved