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?
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With