Destructors And Interfaces

Destructor, Straightforward Usage

  • Lets say one of our sensor implementations allocates resources (e.g. by using new)

  • ⟶ Destructor needed!

  • Called at exit from function

#include <iostream>

class Sensor
{
public:
    virtual double get_value() const = 0;
};

class MySensor : public Sensor
{
public:
    MySensor(double value)
    : _value(new double{value}) {}                     // <-- allocation
    ~MySensor()
    {
        std::cout << "MySensor::~MySensor()" << std::endl;
        delete _value;                                 // <-- deallocation
    }
    double get_value() const override
    {
        return *_value;
    }
private:
    double* _value;
};
#include "sensors.h"
#include <iostream>

int main()
{
    MySensor ms{37.3};
    return 0;                                          // <-- destructor called
}
$ ./code/destructor-nonvirtual/cxx-inher-oo-destructor-straightforward
MySensor::~MySensor()

Calling Destructor Through Base Class Pointer

  • What if MySensor object is

    • allocated on the heap?

    • only a Sensor* is used to represent it? (Polymorphic usage)

    • freed using a Sensor*?

#include "sensors.h"
#include <iostream>

int main()
{
    Sensor* s = new MySensor{37.3};
    delete s;
    return 0;
}
$ ./code/destructor-nonvirtual/cxx-inher-oo-destructor-base-delete
... remains silent ...

Virtual Destructor (Destructors Are Special)

  • Obviously, dynamic dispatch is needed for destructor too

  • Compiler inserts code to call all destructors along the inheritance chain, up to the very base

  • ⟶ Cannot be virtual

  • ⟶ Empty virtual

Solution: Empty Virtual Destructor

#include <iostream>

class Sensor
{
public:
    virtual ~Sensor() = default;                       // <-- empty!
    virtual double get_value() const = 0;
};

class MySensor : public Sensor
{
public:
    MySensor(double value)
    : _value(new double{value}) {}
    ~MySensor() override
    {
        std::cout << "MySensor::~MySensor()" << std::endl;
        delete _value;
    }
    double get_value() const override
    {
        return *_value;
    }
private:
    double* _value;
};
#include "sensors.h"
#include <iostream>

int main()
{
    Sensor* s = new MySensor{37.3};
    delete s;                                          // <-- base pointer used to free derived object
    return 0;
}
$ ./code/destructor-virtual-nonempty/cxx-inher-oo-destructor-virtual-nonempty
MySensor::~MySensor()