Search code examples
c++memory-managementc++14destructorvirtual-destructor

How to properly implement a C++ class destructor


In a class (without direct pointer members), I see there are following 3 possibilities for defining a destructor.

class Child : public Parent
{
public:
    // ~Child() override {}          // (1) explicit destructor with empty body
    // ~Child() override = default;  // (2) explicit default destructor
    //                               // (3) implicit default destructor


private:
    // members
}

Can/should option (1) always be avoided? Because Clang-Tidy hints me to take option (2) if I use option (1).

What are the differences between the three different options in general? What should be considered when selecting one over others?


Solution

  • Can/should option (1) always be avoided?

    Assuming non-ancient version of the language, yes. As far as I can tell, only reason to use an empty non-default destructor is to support C++03 and older standard.

    What should be considered when selecting one over others?

      1. and 3. Have the advantage of being valid in all versions of C++ (disregrading the override specifier).
      1. and 3. Have the advantage of being trivial as long as members are trivially destructable.
      1. and 2. have the advantage of allowing the destructor to be defined separately from the class definition (which was not taken advantage of in the example). This is crucial for example if you have a unique pointer to incomplete type as a member. This is typical when implementing the PIMPL pattern.
      1. and 2. also have the advantage of allowing the destructor be explicitly declared virtual, which is usually necessary for polymorphic base classes.
      1. Has the disadvantage of making the use of implicitly declared copy constructor and assignment operator deprecated. This means it should not be relied on, and may potentially stop working in future. Both 1. and 2. have the disadvantage of preventing implicit move constructor and assignment operator generation. So, if you use either, then you should also declare the copy and move constructors and assignment operators (as defaulted if possible).
      1. Has the advantage of being least amount to write and least amount to read, especially considering the previous paragraph.

    As a rough rule of thumb, use 3. if possible. If not possible (for example, the PIMPL case described above), then use 2. If not possible (i.e. you need to support C++03), then use 1.