C++ Destructors

June 1, 2014

All references are in relation C++11 Standard

All about C++ destructors

What are they ?

A destructor is used to destroy an object of its class type - C++11 §12.4.2

It undoes what a constructor would do.

What is the destruction order ?

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X's direct non-variant non-static data members, the destructors for X' direct base classes and, if X is the type of the most derived class ( 12.6.2), its destructor calls the destructors for X's virtual base classes. All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed in the reverse order of the completion of their constructor (see 12.6.2). - C++11 §12.4.8

Assuming:

namespace s3 {

    class Fu {
    public:
        ~Fu();
    };

    Fu::~Fu() {
        std::cout << "~Fu()" << std::endl;
    }


    class Bar {
    public:
        ~Bar();
    };

    Bar::~Bar() {
        std::cout << "~Bar()" << std::endl;
    }


    class Foo {
    public:
        int value;
        ~Foo();
    };

    Foo::~Foo() {
        std::cout << "~Foo()" << std::endl;
    }


    class Baz : public Foo {
    public:
        Fu fu;
        Bar bar;
        ~Baz();
    };

    Baz::~Baz() {
        std::cout << "~Baz()" << std::endl;
    }

    void main() {
        std::cout << __FILE__ "\n";
        std::cout << "C++ destructor order\n";
        s3::Baz();
        // Prints:
        // ~Baz()
        // ~Bar()
        // ~Fu()
        // ~Foo()
    }
}
  1. Execute Baz destructor body code: Baz::~Baz();
  2. Destroy Baz direct non non variant non static data member: fu and bar
  3. Call Baz direct base classes: Foo::~Foo()

NOTE: Programmer does not need to call the base destructor.

When are destructor called ?

Destructors are invoked implicitly - for constructed objects with static storage duration ( 3.7.1) at program termination ( 3.6.3), - for constructed objects with thread storage duration ( 3.7.2) at thread exit, - for constructed objects with automatic storage duration ( 3.7.3) when the block in which an object is created exits ( 6.7), - for constructed temporary objects when the lifetime of a temporary object ends ( 12.2), - for constructed objects allocated by a new-expression ( 5.3.4), through use of a delete-expression ( 5.3.5), - in several situations due to the handling of exceptions ( 15.3). ... Destructors can also be invoked explicitly - C++11 §12.4.11

Non virtual vs virtual destructors

** Non virtual desctructor **

namespace s1 {

    class Foo {
    public:
        ~Foo();
    };


    Foo::~Foo() {
        std::cout << "~Foo()" << std::endl;
    }

    class Baz : public Foo {
    public:
        ~Baz();
    };

    Baz::~Baz() {
        std::cout << "~Baz()" << std::endl;
    }

    void main() {
        std::cout << __FILE__ "\n";
        std::cout << "C++ Subtyping / inclusion polymorphism WITHOUT \"virtual\" methods\n";

        Foo foo;
        // Prints:
        // ~Foo()

        Baz baz;
        // Prints:
        // ~Baz()
        // ~Foo()

        Foo* baz_0 = new Baz();
        delete baz_0;
        // Prints:
        // ~Foo()

        // One woud expect:
        // ~Baz()
        // ~Foo()

    }
}

A non Virtual destructor will not call the destructor for the instance type Bazbut the base class type Foo.

** Virtual desctructor **

namespace s0 {

    class Foo {
    public:
        virtual ~Foo();
    };


    Foo::~Foo() {
        std::cout << "~Foo()" << std::endl;
    }

    class Baz : public Foo {
    public:
        ~Baz();
    };

    Baz::~Baz() {
        std::cout << "~Baz()" << std::endl;
    }

    void main() {
        std::cout << __FILE__ "\n";
        std::cout << "C++ Subtyping / inclusion polymorphism WITH \"virtual\" methods\n";

        Foo foo;
        // Prints:
        // ~Foo()

        Baz baz;
        // Prints:
        // ~Baz()
        // ~Foo()

        Foo* baz_0 = new Baz();
        delete baz_0;
        // Prints:
        // ~Baz()
        // ~Foo()
    }
}

A Virtual destructor guarantees the destructor for the instance type Baz is called and not the base class type Foo.

When to use non virtual and virtual destructor ?

Use virtual destructor IF and ONLY IF the base class has polymorphic behaviour (has virtual functions) - Effective C++ item 7

Why ?

To call the correct destructor when the instance is casted to a base class. This will ensure the whole momory is free.

How ?

Provide a virtual destructor or pure virtual destructor. If you provide a virtual destructor, make sure the destructor is defined:

class Foo {
 ~Foo() = 0;
 }

 Foo:~Foo{}

A pure virtual destructor's definition is required due to C++ automatically calling the base class destructor. Hence the required destructor definition.

Discussion, links, and tweets

I'm a developer at IO Stark.