operator delete[] does not call derived class destructor

Hi,

The following program creates an array of 3 derived objects and
deletes them. However delete does not seem to call ~Derived( ) ,
i.e., destructor.

---------- derived_delete.cpp -----------
#include <iostream>

class Base
{
public:
  Base() { }

  virtual ~Base() {
    std::cout << "In destructor " << __func__ << std::endl;
  }
};

class Derived: public Base
{
public:
  Derived() {}

  ~Derived() {
    std::cout << "In destructor for " << __func__ << std::endl;
  }
};

int main()
{

  Base* ptr = new Derived[3];
  delete ptr;
  return 0;
}

---------- derived_delete.cpp -----------

The expected output is (e.g. with g++ or icpc):
In destructor for ~Derived
In destructor ~Base
In destructor for ~Derived
In destructor ~Base
In destructor for ~Derived
In destructor ~Base

but the actual output is:
In destructor ~Base
In destructor ~Base
In destructor ~Base

I'm running Clang v3.0 on x86_64 (Debian Squeeze).

Is there any existing bug report for this ?

PS: I'm not subscribed to the list, please keep me in cc.

Hello,

It seems to me that this is undefined behavior:

[expr.delete]/3: 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.

Dmitri

That's correct. I just wanted to add that if you use std::unique_ptr (available in libc++ / -stdlib=libc++), then this run time error will be converted into a compile-time error:

#include <iostream>
#include <memory>

class Base
{
public:
Base() { }

virtual ~Base() {
   std::cout << "In destructor " << __func__ << std::endl;
}
};

class Derived: public Base
{
public:
Derived() {}

~Derived() {
   std::cout << "In destructor for " << __func__ << std::endl;
}
};

int main()
{
  std::unique_ptr<Base> ptr(new Derived[3]);
}

test.cpp:26:27: error: no matching constructor for initialization of 'std::unique_ptr<Base >'
  std::unique_ptr<Base> ptr(new Derived[3]);
                          ^ ~~~~~~~~~~~

Howard

Thanks Dmitri and Howard. Given this declaration:

Base* ptr = new Derived[3];

is there any other "fool-proof" alternative to ensure that the
destructor of Derived objects are always called ? Since it works for
scalar cases, like,

Base* ptr = new Derived;
delete ptr;

as an end user, I expect the array version also to behave as such. I
wonder why the standards decided to make this as "undefined" behavior.

--Satish. BD

I recommend turning on C++11 support (-std=c++11), turning on libc++ (-stdlib=libc++) and then using vector<unique_ptr<Base>>:

#include <iostream>
#include <memory>
#include <vector>

class Base
{
public:
Base() { }

virtual ~Base() {
   std::cout << "In destructor " << __func__ << std::endl;
}
};

class Derived: public Base
{
public:
Derived() {}

~Derived() {
   std::cout << "In destructor for " << __func__ << std::endl;
}
};

int main()
{
  typedef std::unique_ptr<Base> BasePtr;
  std::vector<BasePtr> ptr;
  ptr.push_back(BasePtr(new Derived));
  ptr.push_back(BasePtr(new Derived));
  ptr.push_back(BasePtr(new Derived));
}

In destructor for ~Derived
In destructor ~Base
In destructor for ~Derived
In destructor ~Base
In destructor for ~Derived
In destructor ~Base

Howard

Thanks Howard, works well for me :slight_smile:

I think the lesson for clang development is that this code should have produced a warning. Could it ever make sense to do a derived-to-base conversion on a pointer that came out of array new? I don't think so.

bd satish wrote:

Since it works for scalar cases, like,

Base* ptr = new Derived;
delete ptr;

as an end user, I expect the array version also to behave as such. I wonder why the standards decided to make this as "undefined" behavior.

To understand why, let's think about what the operations do. First, the non-array case, what delete does is:

- Call the destructor (possibly via dynamic dispatch)
- call operator delete (void* ptr), where ptr is the start of the object

In contrast, for arrays, what it needs to do is:

- Call the destructors for all the objects in the array (starting at the *end* of the array and working backwards)
- call operator delete (void* ptr), where ptr start of the memory block previously allocated with operator new

It's the first of these things that is problematic. How can the compiler know where that last element is. It does know that the array has three things in it, but it can't use n*sizeof(Base) to calculate the value it needs, which is n*sizeof(Derived).

In general, this is why Derived is not a subtype of Base -- array indexing just doesn't work. Thus, what you're trying to do is *fundamentally misguided*. You need to work an array of pointers to objects (which is what Java does under the covers with its arrays of objects), or be *incredibly* painstakingly careful.

    M.E.O.

P.S. For basic C++ questions, you might be better off asking on a site devoted to such things, such as StackOverflow, rather than the a compiler development mailing list.

P.P.S. But, for fun witnessing undefined behavior, try this:

#include <iostream>

class Base {
public:
    Base()
    {
        std::cout << "Creating Base at " << this << std::endl;
    }

    virtual ~Base()
    {
        std::cout << "Destroying Base at " << this << std::endl;
    }
};

class Derived: public Base {
public:
    Derived()
    {
        std::cout << "Creating Derived at " << this << std::endl;
    }

    ~Derived() {
        std::cout << "Destroying Derived at " << this << std::endl;
    }
    
    int stuff_; // sizeof(Derived) > sizeof(Base) now
};

int main()
{

    Base* ptr = new Derived[3];
    delete ptr;
    return 0;
}

It produces

Creating Base at 0x1072008f8
Creating Derived at 0x1072008f8
Creating Base at 0x107200908
Creating Derived at 0x107200908
Creating Base at 0x107200918
Creating Derived at 0x107200918
Destroying Base at 0x107200908
Destroying Base at 0x107200900
Destroying Base at 0x1072008f8

Notice that it destroys objects at the wrong places. It destroys an object at 0x107200900 where none exists, and never touches the one at 0x107200918.

Thanks O'Neill for the explanation.

I posted the question here because g++ and icpc seemed to do the right
thing (or so I thought) but clang++ gave different result. Anyways,
thanks for the info !

* M. E. O'Neill:

In contrast, for arrays, what it needs to do is:

- Call the destructors for all the objects in the array (starting at
  the *end* of the array and working backwards)
- call operator delete (void* ptr), where ptr start of the memory
  block previously allocated with operator new

It's the first of these things that is problematic. How can the
compiler know where that last element is. It does know that the
array has three things in it, but it can't use n*sizeof(Base) to
calculate the value it needs, which is n*sizeof(Derived).

The element size could be written next to the place where the number
of elements is stored. Or it could be derived from the vtable pointer
(which is the same for all the elements). Probably not without ABI
changes, though.

Yes, it would require ABI changes. Also, you'd presumably want this
"polymorphic array" handling to work with subscripting and other kinds
of pointer arithmetic, which would be a substantial unrecoverable
overhead when dealing with arrays whose elements happen to have
polymorphic type.

Anyway, the fundamental language brokenness here is that new
just returns a pointer instead of some sort of slice type. The confusion
of pointers and arrays is a C legacy that works out well enough there
but is quite problematic in C++ for many reasons, of which this is one.

John.

Florian Weimer wrote:

[...] How can the compiler know where that last element is. It does know that the array has three things in it, but it can't use n*sizeof(Base) to calculate the value it needs, which is n*sizeof(Derived).

The element size could be written next to the place where the number of elements is stored.

Doing that would violate a core principle of C++, which is "you don't pay for what you don't use", since it would add overhead for every array created with new. Most people wouldn't use this feature, yet would have to pay for it.

In fact (as I understand it), early in the design of C++, new didn't stash the array size and you had remember it yourself and then pass it into delete (inside the square brackets). Although Stroustrup changed it pretty quickly to the design we know today because people hated that hassle, I think the original design would have been more true to the C++ philosophy.

Or it could be derived from the vtable pointer (which is the same for all the elements). Probably not without ABI changes, though.

Yes, we can imagine the type_info struct containing size information, but that would still increase the per-class overhead of RTTI, and as we just saw in another thread, some people build with -fno-rtti just to avoid the overhead we have now.

Myself, I'd love it it C++ had better run-time metadata about types, even if it were only optional and only enabled with -fmore-rtti-please. As things stand right now, the only way to introspect in a meaningful way is to either (a) use facilities intended for the debugger, or (b) adopt a coding convention that hand-supplies the necessary metadata, duplicating information clang already knows. I don't like (b) on principle.

FWIW, as an example of applying strategy a to the problem at hand, you can can use LLDB's SBType::GetByteSize, as in the enclosed code. Getting from a typeid to an SBType inside the original program is left as an exercise for the reader.

    M.E.O.

Enc.

#!/usr/bin/python
# Look up a type in the debugging information. No error checking.
import lldb
import sys
prog,type = sys.argv[1:3]
debugger = lldb.SBDebugger.Create()
debugger.SetAsync(False)
target = debugger.CreateTargetWithFileAndArch (prog, lldb.LLDB_ARCH_DEFAULT)
desiredtype = target.FindTypes(type).GetTypeAtIndex(0);
print desiredtype
print "---> size is", desiredtype.GetByteSize()

unix% clang -g -o badcode badcode.cpp
unix% python typeinfo.py badcode Base
class Base {
    Base();
    virtual void ~Base();
}
---> size is 8
unix% python typeinfo.py badcode Derived
class Derived : public Base {
    int stuff_;
    Derived();
    virtual void ~Derived();
}
---> size is 16

* M. E. O'Neill:

In fact (as I understand it), early in the design of C++, new didn't
stash the array size and you had remember it yourself and then pass
it into delete (inside the square brackets). Although Stroustrup
changed it pretty quickly to the design we know today because people
hated that hassle, I think the original design would have been more
true to the C++ philosophy.

Interesting. For current C++, I would agree. But in the pre-template
age without std::vector, that would have been a bit harsh.

Myself, I'd love it it C++ had better run-time metadata about types,
even if it were only optional and only enabled with
-fmore-rtti-please.

Or they could be emitted as soon as you instantiate std::type_info<T>
for some type T.

As things stand right now, the only way to introspect in a
meaningful way is to either (a) use facilities intended for the
debugger, or (b) adopt a coding convention that hand-supplies the
necessary metadata, duplicating information clang already knows. I
don't like (b) on principle.

There's also (c), use a code generator (possibly as a compiler
plug-in).