Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why must I implement concrete destructor on abstract class? [duplicate]

I am new to C++11, and I am trying to build a classic interface / implementation pair. Consider the following sample:

#include <iostream>
#include <memory>

// Interface
class IFace {
public:
    virtual ~IFace () = 0;
};
// Why must I define this concrete implementation?
inline IFace::~IFace () { }

// Implementation
class Impl : public IFace {
public:
    virtual ~Impl () { }
};

int main (int argc, char ** argv) {
    auto impl = std::shared_ptr<Impl> (new Impl ());
    return 0;
}

If I comment out the undesirable concrete destructor on the interface inline IFace::~IFace () { }, I get link errors

Undefined symbols for architecture x86_64:
  "IFace::~IFace()", referenced from:
      Impl::~Impl() in ifac_impl.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I must do this even if the interface and implementation are templated:

// Template IFace2
template <typename T>
class IFace2 {
public:
    virtual ~IFace2 () = 0;
};
// I have to do this even if the pair is templated.
template <typename T>
inline IFace2<T>::~IFace2<T> () { }

template <typename T>
class Impl2 : public IFace2<T> {
public:
    virtual ~Impl2 () { }
};

int main (int argc, char ** argv) {
    auto impl = std::shared_ptr<Impl> (new Impl ());
    auto impl2 = std::shared_ptr<Impl2<double> > (new Impl2<double> ());
    return 0;
}

Why?

A second question is "am I going about this the wrong way?" That is, is there a better pattern (idiom?) for what I want to do? I am admittedly trying to fit a conceptual pattern I developed with C# into C++11. Is this sane? If not, what is the sane way?

like image 559
Reb.Cabin Avatar asked Jan 22 '26 10:01

Reb.Cabin


2 Answers

When you override a virtual function in the derived class, and then invoke it, the base implementation is not called, unless you explicitly call it.

For instance,

struct base
{
  virtual void foo() {}
};

struct derived : base
{
  void foo() override {}
};

base *b = new derived;
b->foo();  // this will not call base::foo

The same applies to pure virtual functions as well.

However, in the case of a (pure) virtual destructor, the base destructor will be called (because the base sub-object needs to be destroyed). Hence, if you have a pure virtual destructor, you must provide an implementation. Otherwise you'll never be able to instantiate any derived class.

like image 153
Praetorian Avatar answered Jan 26 '26 00:01

Praetorian


The destructor should not be pure virtual. That would make the class abstract and therefore not possible to instantiate. Just define it as default:

class IFace {
public:
    virtual ~IFace () = default;
};

Live demo

like image 38
Shoe Avatar answered Jan 25 '26 23:01

Shoe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!