Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Destructors FAQ

Last updated Jan 1, 2003.

The destructor FAQ completes the FAQ trilogy which includes the Constructors FAQ and the Copy Constructor and Assignment Operator FAQ series. This Q&A format article explains key concepts of the C++ destructors and shows how to avoid common pitfalls and misunderstandings.

Question: Tell Me More About An Implicitly-Declared Destructor. When Is Such A Destructor Implicitly-Defined?

The compiler implicitly-declares a destructor in every class or struct that don’t have a user-declared destructor. Notice that unions cannot have a destructor at all, so the compiler doesn’t implicitly declare or define destructors in unions.

An implicitly-declared destructor is an inline public member of its class. A destructor is trivial if it is an implicitly-declared destructor and if:

  • All of the direct base classes of its class have trivial destructors and
  • For all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.

Otherwise, the destructor is non-trivial. A user-declared destructor isn’t trivial either, according to this definition. An implicitly-declared destructor is implicitly defined when it is used to destroy an object of its class type. A program is ill-formed if the class for which a destructor is implicitly defined has:

  • A nonstatic data member of class type (or array thereof) with an inaccessible destructor or
  • A base class with an inaccessible destructor.

Before the implicitly-declared destructor for a class is implicitly defined, all the implicitly-declared destructors for its base classes and its nonstatic data members shall have been implicitly defined as well. In simpler words, an implicitly-defined destructor has one purpose, namely, to invoke all non-trivial destructors of the base class(es) and embedded objects.

Question: Does a User-Defined Empty Destructor Have any Role?

Answer: Just as with empty user-defined constructors, some programmers insist on defining empty destructors in their class:

struct A
~A() {}

Such as destruct has no effect. However, it does change the semantics of struct A, making it a non-POD type. So while the destructor doesn’t perform any cleanup operation in this case, it may be used as a means of making a class a non-POD type. That said, I’m under the impression that most empty destructors have absolutely no purpose. They’re defined because programmers mistakenly believe that every class must have a destructor, which isn’t correct.

Question: I Defined A Constructor For My Class. Do I Have To Define A Destructor As Well?

Answer: Not really. Whether your class needs a destructor is determined by one criterion: does the object need to perform any cleanup operations before it’s destroyed. Most programmers are used to think of a destructor as a means for releasing a resource that was acquired during the object’s lifetime. That is correct. However, there are many objects that don’t acquire any resources at all, ever. Certain function objects for example never acquire any resources. Clearly, such objects don’t have to release any resources during destruction. Hence, they don’t need destructors. However, destructors may have different purposes such as writing to a log file, or printing a message on the user’s screen. To conclude, the decision whether to define a destructor isn’t automatic. It depends on the design and the use of the class. A class that has a user-declared constructor but doesn’t have a user-declared destructor isn’t necessarily an error.

Question: When Is It Necessary To Invoke A Destructor Explicitly? How Do I Do It?

Answer: In the majority of cases, you don’t have to invoke a destructor explicitly. In fact, most C++ programmers have never used this feature at all. An explicit destructor invocation is needed when an object is allocated on a predetermined memory address using placement-new. If you’re using placement-new to create an object, you must destroy that object explicitly before releasing its storage. In the Copy Constructor and Assignment FAQ series you saw a programming idiom that necessitates an explicit destructor invocation too.

To invoke a destructor explicitly, simply call it as an ordinary member function of the object. A destructor in C++ has a name (unlike a constructor, which doesn’t have a name). Here’s an example of an explicit destructor invocation:

A a;
a.~A(); //explicit destructor invocation

The name of the destructor is identical to its class name, preceded by a ~ sign. Another example:

A * pa= new A;

The two explicit destructor invocations above use the non-qualified name of the destructor. You can also use the qualified name of the destructor in an explicit invocation:

A a;
a.A::~A(); //explicit destructor invocation
A * pa= new A;

The qualified and non-qualified forms aren’t interchangeable. Whereas the non-qualified call will resolve the call (if the destructor is virtual), the qualified call is always resolved statically. Thus, of pa happens to be pointing to a derived object, the explicit destructor invocation with the qualified name will always be resolved as a call to A’s destructor (which is most likely a bug), whereas the non-qualified call will be resolved dynamically, invoking the derived object’s destructor first (which will in turn invoke base classes’ destructors).

Question: Can I Pass Parameters To A Destructor?

Answer: Destructors cannot take any parameters and they have no return type at all -- not even void. Passing parameters to a destructor doesn’t make any sense because in the overwhelming majority of cases, the destructor is invoked implicitly. How can you pass arguments to a function that you don’t call directly? For a similar reason, a destructor cannot have a return type. The only two qualifiers allowed for a destructor are inline and virtual.

A destructor cannot be a const or volatile member function either. const and volatile semantics are not applied on an object under destruction, even if that object was declared const or volatile:

const A * pa = new A;
delete pa; // OK, *pa is no longer const once the destructor has been invoked.

Question: What Are the Common Destructor Pitfalls?

Answer: Apart from ordinary bugs, such as releasing a resource that has already been released or forgetting to release a resource, destructors are particularly vulnerable to two design mistakes.

  • Failing to declare a virtual destructor in a base class
  • Failing to implement a pure virtual destructor

A class that is meant to serve as a base class must define a virtual destructor, even if that destructor is empty. Remember: if you don’t declare a destructor, the compiler will implicitly declare (and in some cases, define) a destructor for that class. Such an implicitly-declared destructor is never virtual. In a similar vein, if a class doesn’t have a virtual destructor, you shouldn’t derive from it. STL containers are a classic example. Deriving a class from std::string is always a bad idea, as tempting as it may seem at first.

Pure virtual functions are meant to be overridden in a derived class. Normally, you don’t implement such functions (although it’s possible to do so) because pure virtual functions should never be invoked. And yet, pure virtual destructors are a special case. They must always be defined. The reason for that is the unique behavior or virtual destructors -- a destructor in a derived class doesn’t override a base class destructor.

The definition of a pure virtual destructor must appear outside the class body:

struct interface
 virtual ~interface()=0;