Best way to declare an interface in C++11
As we all know, some languages have the notion of interfaces. This is Java:
public interface Testable {
void test();
}
How can I achieve this in C++ (or C++11) in most compact way and with little code noise? I'd appreciate a solution that wouldn't need a separate definition (let the header be sufficient). This is a very simple approach that even I find buggy ;-)
class Testable {
public:
virtual void test() = 0;
protected:
Testable();
Testable(const Testable& that);
Testable& operator= (const Testable& that);
virtual ~Testable();
}
This is only the beginning.. and already longer that I'd want. How to improve it? Perhaps there is a base class somewhere in the std namespace made just for this?
For dynamic (runtime) polymorphism, I would recommend using the Non-Virtual-Interface (NVI) idiom. This pattern keeps the interface non-virtual and public, the destructor virtual and public, and the implementation pure virtual and private
class DynamicInterface
{
public:
// non-virtual interface
void fun() { do_fun(); } // equivalent to "this->do_fun()"
// enable deletion of a Derived* through a Base*
virtual ~DynamicInterface() = default;
private:
// pure virtual implementation
virtual void do_fun() = 0;
};
class DynamicImplementation
:
public DynamicInterface
{
private:
virtual void do_fun() { /* implementation here */ }
};
The nice thing about dynamic polymorphism is that you can -at runtime- pass any derived class where a pointer or reference to the interface base class is expected. The runtime system will automatically downcast the this
pointer from its static base type to its dynamic derived type and call the corresponding implementation (typically happens through tables with pointers to virtual functions).
For static (compile-time polymorphism), I would recommend using the Curiously Recurring Template Pattern (CRTP). This is considerably more involved because the automatic down-casting from base to derived of dynamic polymporphism has to be done with static_cast
. This static casting can be defined in a helper class that each static interface derives from
template<typename Derived>
class enable_down_cast
{
private:
typedef enable_down_cast Base;
public:
Derived const* self() const
{
// casting "down" the inheritance hierarchy
return static_cast<Derived const*>(this);
}
Derived* self()
{
return static_cast<Derived*>(this);
}
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~enable_down_cast() = default; // C++11 only, use ~enable_down_cast() {} in C++98
};
Then you define a static interface like this:
template<typename Impl>
class StaticInterface
:
// enable static polymorphism
public enable_down_cast< Impl >
{
private:
// dependent name now in scope
using enable_down_cast< Impl >::self;
public:
// interface
void fun() { self()->do_fun(); }
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~StaticInterface() = default; // C++11 only, use ~IFooInterface() {} in C++98/03
};
and finally you make an implementation that derives from the interface with itself as parameter
class StaticImplementation
:
public StaticInterface< StaticImplementation >
{
private:
// implementation
friend class StaticInterface< StaticImplementation > ;
void do_fun() { /* your implementation here */ }
};
This still allows you to have multiple implementations of the same interface, but you need to know at compile-time which implementation you are calling.
So when to use which form? Both forms will let you re-use a common interface and inject pre/post condition testing inside the interface class. The advantage of dynamic polymorphism is that you have runtime flexibility, but you pay for that in virtual function calls (typically a call through a function pointer, with little opportunity for inlining). Static polymporhism is the mirror of that: no virtual function call overhead, but the disadvantage is that you need more boilerplate code and you need to know what you are calling at compile-time. Basically an efficiency/flexiblity tradeoff.
NOTE: for compile-time polymporhism, you can also use template parameters. The difference between static interface through the CRTP idiom and ordinary template parameters is that CRTP-type interface are explicit (based on member functions), and template interface are implicit (based on valid expressions)
What about:
class Testable
{
public:
virtual ~Testable() { }
virtual void test() = 0;
}
In C++ this makes no implications about copyability of child classes. All this says is that the child must implement test
(which is exactly what you want for an interface). You can't instantiate this class so you don't have to worry about any implicit constructors as they can't ever be called directly as the parent interface type.
If you wish to enforce that child classes implement a destructor you can make that pure as well (but you still have to implement it in the interface).
Also note that if you don't need polymorphic destruction you can choose to make your destructor protected non-virtual instead.
According to Scott Meyers (Effective Modern C++): When declaring interface (or polymorphic base class) you need virtual destructor, for proper results of operations like delete
or typeid
on a derived class object accessed through a base class pointer or reference.
virtual ~Testable() = default;
However, a user-declared destructor suppresses generation of the move operations, so to support move operations you need to add:
Testable(Testable&&) = default;
Testable& operator=(Testable&&) = default;
Declaring the move operations disables copy operations and you need also:
Testable(const Testable&) = default;
Testable& operator=(const Testable&) = default;
And the final result is:
class Testable
{
public:
virtual ~Testable() = default; // make dtor virtual
Testable(Testable&&) = default; // support moving
Testable& operator=(Testable&&) = default;
Testable(const Testable&) = default; // support copying
Testable& operator=(const Testable&) = default;
virtual void test() = 0;
};
Another interesting article here: The Rule of Zero in C++
By replacing the word class
with struct
, all of the methods will be public by default and you can save a line.
There's no need to make the constructor protected, since you can't instantiate a class with pure virtual methods anyway. This goes for the copy constructor as well. The compiler-generated default constructor will be empty since you don't have any data members, and is completely sufficient for your derived classes.
You're right to be concerned about the =
operator since the compiler-generated one will certainly do the wrong thing. In practice nobody ever worries about it because copying one interface object to another never makes sense; it's not a mistake that happens commonly.
Destructors for an inheritable class should always be either public and virtual, or protected and non-virtual. I prefer public and virtual in this case.
The final result is only one line longer than the Java equivalent:
struct Testable {
virtual void test() = 0;
virtual ~Testable();
};