Deleting object with private destructor
Asked Answered
S

3

10

How is that possible that it is allowed to delete object with private destructor in the following code? I've reduced real program to the following sample, but it still compiles and works.

class SomeClass;

int main(int argc, char *argv[])
{
  SomeClass* boo = 0; // in real program it will be valid pointer
  delete boo; // how it can work?

  return -1;
}

class SomeClass
{
private:
  ~SomeClass() {}; // ! private destructor !
};
Siena answered 23/10, 2009 at 6:54 Comment(2)
Interesting..if I move the definition of the class above main() then it throws a compiler error. Otherwise, I just get a warning C4150: deletion of pointer to incomplete type 'SomeClass'; no destructor calledMitzimitzie
@Naveen: That's expected. Incomplete type is one problem. Private destructor is another. Each has its own diagnostic message. You switch between the two by moving the definition of SomeClass.Partition
J
15

You are trying to delete object of incomplete class type. C++ Standard says that you'll get undefined behavior in this case (5.3.5/5):

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.

To detect such cases you could use boost::checked_delete:

template<typename T> 
inline void checked_delete( T* p )
{
    typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
    (void) sizeof(type_must_be_complete);
    delete p;
}
Japonica answered 23/10, 2009 at 7:1 Comment(10)
This takes advantage of sizeof() never returning 0 even for empty structs/classes, which I'd forgotten. ISO-IEC-14882 section 9.1: "Complete objects and member subobjects of class type shall have nonzero size." The implication is that every object in an array will have a unique memory address. Presumably same for structs -- is that so in C as well?Commissary
In C size of empty struct is equal to 0.Japonica
@leander: Not exactly. You see in C++ it is simply illegal to apply sizeof to an incomplete type. This is what the above code is trying to catch by (void) sizeof ... part. This expression shall stop compilation on a compiler that complies to the standard by issuing an error. However, if some strange compiler happens to allow sizeof on incomplete types (as an extension) with 0 result, then the typedef ... part will catch those and force "an array of negative size" failure. So, you have a main "trap" here (the (void) part) and auxiliary trap "just in case" (the typedef part).Partition
@leander: In C the situation is the same: it is illegal to apply sizeof to an incomplete type. So, if some C compilers return 0 in this case, it is their own extension that has little to do with C language.Partition
I don't know what Kirill means by "in C size of empty struct is equal to 0" and why. Firstly, an incomplete struct type is not "an empty struct". Secondly, in C it is impossible to declare an empty struct (i.e. a struct with no members) - it is explicitly illegal. If some C compiler allows it as an extension, the size migtht be 0, but that has little to do with C language.Partition
@leander: Oh, I see. It was actually you who brought "empty structs/classes" into the picture... No, it has nothing to do with "empty structs/classes" at all. It is about incomplete types. "Incomplete" and "empty" are two entirely different and absoltely unrelated things.Partition
@AndreyT, I was talking about empty structs, not incomplete types. Leander asked: whether sizeof for empty struct is nonzero. C Standard doesn't guarantees that, but C++ Standard does.Japonica
@Kirill: OK, I already realized why you are talking about "empty structs" (see my comment above). But nevertheless, your statement "In C size of empty struct is equal to 0" is incorrect, or misleading at best. There's simply no such thing as "empty struct" in C. That's all I was trying to say. And again (this is to leander), "empty structs" are not relevant here at all.Partition
@Kirill" struct X {}; is empty and it causes compilation error (or, more formally, "diagnostic message") on any compliant C compiler. Because, as I said above, C does not allow empty structs.Partition
I see now, I missed that struct X{}; is invalid in Standard C. I've compiled it in gcc and it doesn't warned me about an error.Japonica
P
8

This code causes undefined behavior (UB). It is UB in C++ to delete an object of incomplete type having a non-trivial destructor. And in your code the type SomeClass is incomplete at the point of delete, and it has a non-trivial destructor. Compilers usually issue a warning about this, since in C++ formally this is not a constraint violation.

So, strictly speaking, your code doesn't "work". It simply compiles and does something undefined when run.

The compiler is just not required to catch this error. The reason for this is that this could be perfectly fine if your object has a trivial destructor. The compiler has no way of knowing what kind of destructor this type will eventually have, so it can't say for sure whether this is an error or not.

Partition answered 23/10, 2009 at 7:0 Comment(1)
It would be fine if (1) T has a trivial destructor (2) T doesn't redefine `delete operator``Where
P
4

Because SomeClass type is not completely declared when invoking operator delete.

Deleting such a pointer is undefined behavior, but in practice most compilers would just free the memory (if the pointer was non-NULL) and not call the destructor.

For example, g++ will give you a warning about this issue:

foo.cpp: In function 'int main(int, char**)':
foo.cpp:6: warning: possible problem detected in invocation of delete operator:
foo.cpp:5: warning: 'boo' has incomplete type
foo.cpp:1: warning: forward declaration of 'struct SomeClass'
foo.cpp:6: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
Padnag answered 23/10, 2009 at 7:1 Comment(3)
"Deleting such a pointer just would just free the memory..." - NO! The behaviour is UNDEFINED, i.e. any assumptions on what "essentially" happens are invalid.Roofing
@DevSolar: True, it's undefined as per standard. In practice that's what will happen on most compilers. I've updated the answer to reflect this.Padnag
I have somewhat of a problem with relying on anything outside the well-behavioured realm. Especially in code of co-workers. It drives me up the wall. I blame answers like yours. ( ;-) <<-- important smiley)Roofing

© 2022 - 2024 — McMap. All rights reserved.