3
votes

I find myself have troubles in understanding following sentence quoted from C++ standard in 5.3.5$5: (emphasis is mine)

If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.

I know this deleting incomplete type problem has been discussed several times in SO, and I can understand why deleting incomplete class type is undefined behavior. This Q&A explains it very well.

What I can't understand is the part about complete class type. Does it mean deleting a object of complete class has a non-trivial destructor or a deallocation function is undefined behavior? If so, please give some codes illustrating the undefined behavior that it could result in.

3

3 Answers

6
votes

There are two prerequisites for undefined behavior in the concerned case:

  1. Object is being deleted through a pointer to an incomplete type;
  2. The complete class of the object being deleted has a non-trivial destructor or a (user-defined) deallocation function.

If either of these conditions is false, then there is no undefined behavior (at least due to the concern being discussed).

This in particular means that

  1. Deleting an object of complete type is safe (because the right destructor and/or deallocation function will be executed).

  2. Deleting an object with a trivial destructor and without a user-defined deallocation function through a pointer to an incomplete type is safe (because in the absence of complete type information the compiler doesn't call the destructor and uses the default deallocation function, which perfectly matches what would happen had the deletion be performed through a pointer to complete type).

2
votes

Let me try to explain what I understand using the code below:

struct Foo;
struct Bar;

Foo* newFoo();
Bar* newBar();

int main()
{
   // Get a pointer to Foo, somehow
   Foo* fp = newFoo();

   // Get a pointer to Bar, somehow
   Bar* bp = newBar();

   // Foo is an incomplete class at this point.
   // This is OK since Foo has a trivial destructor.
   delete fp;

   // Bar is an incomplete class at this point.
   // Not OK since Bar has a non-trivial destructor.
   // This is cause for undefined behavior.
   delete bp;
}

struct Foo {};

struct Bar {
   Bar() { data = new int[10]; }
   ~Bar(){ delete [] data; }
   int* data;
};

Foo* newFoo()
{
   return new Foo;
}

Bar* newBar()
{
   return new Bar;
}
2
votes

Does it mean deleting a object of complete class has a non-trivial destructor or a deallocation function is undefined behavior?

No, as that would mean that

class Foo {
public:
    ~Foo() { /*do something*/ };
};

is undefined behavior, which isn't the case.

You seem to already know that deleting an object which has an incomplete class type is undefined behavior. The thing is, it is only undefined behavior if the class has a non-trivial destructor/deallocation.

It is not undefined behavior in this case:

//Foo is forward declared somewhere and destructed
//This is the class definition not available at the point that it is destructed
class Foo {
public:
    //~Foo() = default; 
};

It is only undefined in this case

class Foo {
public:
    ~Foo() { /*do something*/ };
};

Do note that even if Foo had a trivial destructor/deallocation, if Foo inherited from another class that had a non-trivial destructor/deallocation, it would still be undefined behavior.