50

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?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
emesx
  • 12,555
  • 10
  • 58
  • 91
  • 2
    Interfaces aren't generally copyable, nor directly constructable, are you really declaring a interface? – MerickOWA Jan 14 '13 at 17:38
  • Here I just want to hide them from public, let the children decide. – emesx Jan 14 '13 at 17:39
  • 2
    Why do you need the protected definitions? – James Jan 14 '13 at 17:39
  • 2
    related: [How can I simulate interfaces in C++?](http://stackoverflow.com/q/1216750/10396) – AShelly Jan 14 '13 at 17:41
  • @James this is just a quick sketch. I am open to many ideas – emesx Jan 14 '13 at 17:42
  • @elmes, let the children decide what? What exactly would Testable's copy/assignment constructor be doing or be assigning? – MerickOWA Jan 14 '13 at 17:43
  • This is a bit of OT, but let me ask: if I don't declare copy ctor and assignment operator protected, then they are defaulted to public? – emesx Jan 14 '13 at 17:45
  • @elmes, yes. The default ones are public. – StoryTeller - Unslander Monica Jan 14 '13 at 17:47
  • Then if they are public once, then they can't be protected somewhere down the inheritance tree, correct? – emesx Jan 14 '13 at 17:47
  • @elmes Yes, but you can mark them in C++11 as "= delete" if you really mean to indicate that copying or assignment isn't allowed. – MerickOWA Jan 14 '13 at 17:48
  • Constructors cannot be virtual and the `= 0` syntax can only be used on virtual methods. If you want to say 'this class does not have this method' then you want the C++11 syntax `= delete`. – bames53 Jan 14 '13 at 17:49
  • 1
    @MerickOWA So I guess now it's clear why I made them protected - to enable children to be copyable. What about `=delete` - is it inherited? Can I implement a method once it's deleted? – emesx Jan 14 '13 at 17:51
  • @elmes: They are public, yes, but they are not the same as what the derived classes define. The derived classes can define their own as private or protected. – Cornstalks Jan 14 '13 at 18:03
  • And still there is "possible duplicate" -http://stackoverflow.com/questions/318064/how-do-you-declare-an-interface-in-c – SChepurin Jan 14 '13 at 18:04
  • @elmes: To add, `Base(const Base&)` should only ever be called by `Derived(const Derived&)` (the user should never (and can never) call it), and the default `Base& operator = (const Base& b)` should never be called either. Don't try to give value semantics to your interfaces; they're intended to be used with reference semantics. Using value semantics will likely result in violating [LSP](http://en.wikipedia.org/wiki/Liskov_substitution_principle). – Cornstalks Jan 14 '13 at 18:12
  • What kind of undesirable usage do you want to prevent when you declare these members `protected`? Can you give code examples? – n. m. could be an AI Jan 14 '13 at 19:17

5 Answers5

47

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)

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • 2
    I've read NVI is nice when you have some common code, like pre or post conditions. What does NVI improve in an Interface declaration? – emesx Jan 14 '13 at 18:45
  • This is the approach preferred by Herb Sutter. Not sure I agree with it since it seems to complicate things unnecessarily, but he makes some good points: http://www.gotw.ca/publications/mill18.htm – Mark Ransom Jan 14 '13 at 18:46
  • 1
    It will allow you to add pre or post conditions later to your class without derived classes having to adapt their code. This flexibility is one advantage of the NVI – TemplateRex Jan 14 '13 at 18:46
  • @MarkRansom Scott Meyers also talks about it in his *Effective* series ;) – emesx Jan 14 '13 at 18:51
  • 1
    "write the non-const version in terms of the const version" Eh, this is a tool used to reuse complicated code, but in this case you've merely made it more complicated. – GManNickG Jan 14 '13 at 19:39
  • @GManNickG Yeah, you're right. The code was from my notes where I experimented a bit with various idioms. `static_cast(this);` is almost certainly easier to read. Updated. – TemplateRex Jan 14 '13 at 19:51
  • I don't understand the benefit / function of creating a non-virtual interface instead of a public virtual interface. Are there drawbacks of the latter? The latter still allows you to, at runtime, pass a derived class instance where a base class reference is specified, correct? – vargonian Jan 29 '18 at 20:02
  • 1
    @vargonian yes, the polymorphism is still maintained with a public virtual interface. However, using public non-virtual interface with protected virtual implementation allows all kinds of assertions to be implemented in the base class. See e.g. this column by Herb Sutter: http://www.gotw.ca/publications/mill18.htm – TemplateRex Jan 31 '18 at 11:06
41

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.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • Can the dtor be pure abstract? – emesx Jan 14 '13 at 17:43
  • 2
    @elmes: No. It needs a definition (but you can leave it empty like Mark B has shown) – Cornstalks Jan 14 '13 at 17:45
  • This is nice, but it implies copying capabilities of the children. A Java interface doesn't impose any functionality on the implementers apart from the iface itself.. – emesx Jan 14 '13 at 17:53
  • @elmes: The children can mark their own copy constructors as private/protected if they want... – Cornstalks Jan 14 '13 at 17:56
  • @elmes Yes, destructor can be pure virtual, but it still needs a body. – sdkljhdf hda Jan 14 '13 at 18:10
  • 13
    @elmes: The destructor can be *pure virtual*, but it must provide a definition (the two are not exclusive). The fact that the interface is copyable does not imply that the objects are, so this interface does not imply that capability. As a matter of fact, copying at the interface level will cause *slicing* and will not be a good idea at any point. – David Rodríguez - dribeas Jan 14 '13 at 18:10
  • Should that be `virtual void test() = delete;` for C++11? – Steve-o Jan 14 '13 at 18:43
  • 1
    @MarkB that is a terrible statement out of context and I'm not even sure in what context such a statement is even useful. `=delete` applies to any member function that is not a (normal, non-copy and non-move) constructor/destructor. – rubenvb Jan 14 '13 at 19:06
  • 4
    @Steve-o: No, it should not be `=delete`. `=delete` says that it's not legal to call it (you'll get an error attempting to call it. `=0` says it's legal, but must be defined by a child class. – Dave S Jan 14 '13 at 19:24
  • 8
    @Cornstalks - `virtual ~Testable() = default;` is preferable to defining your own body in C++ 11 – Steve Folly Jan 26 '15 at 15:27
24

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++

aahzbg
  • 452
  • 3
  • 11
12

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();
};
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • *Destructors ... should always be either public and virtual, or protected and non-virtual*. Why the mutual exclusion? – emesx Jan 14 '13 at 20:28
  • 1
    @elmes, if the destructor is public you'll be tempted to use it and it needs to be virtual to work properly. If it's protected there's no need to make it virtual since only derived classes can call it, and they'll call the base class destructor automatically. Certainly there's no harm in making a protected destructor virtual, it just doesn't do any good. I didn't make up the rule, I'm just repeating it. – Mark Ransom Jan 14 '13 at 20:54
  • What do you mean by *using the dtor*? Simply deleting a derived class via base pointer (`delete b`), or explicitly calling the dtor after *placement new* (`b->~b()`)? Is anybody tempted to use it this way anyway? :) – emesx Jan 14 '13 at 20:58
  • 1
    @elmes, yes I meant deleting a derived class via base pointer. Whether you need that or not depends on how you're handling the lifetime of your objects. It's always safer to assume you'll need it even if you never do. – Mark Ransom Jan 14 '13 at 21:08
7

Keep in mind that the "rule of three" is unnecessary if you aren't managing pointers, handles, and/or all the data-members of the class have their own destructors that will manage any clean-up. Also in the case of a virtual base class, because the base class can never be directly instantiated, it's not necessary to declare a constructor if all you're wanting to-do is define an interface that has no data members ... the compiler defaults are just fine. The only item you would need to keep is the virtual destructor if you are planning on calling delete on a pointer of the interface type. So in reality your interface can be as simple as:

class Testable 
{
    public:
        virtual void test() = 0;  
        virtual ~Testable();
}
Jason
  • 31,834
  • 7
  • 59
  • 78
  • 10
    A virtual destructor is still necessary if there's any possibility of deleting the object via its interface pointer. – Mark Ransom Jan 14 '13 at 17:41
  • 3
    Yes, arguably the destructor should be virtual if it's public, or protected if it isn't virtual. – ChrisW Jan 14 '13 at 17:45
  • why make dtor protected and not public? – emesx Jan 14 '13 at 17:47
  • A virtual protected destructor, as you have in your answer now, isn't useful. It can only be called from a derived class as it's protected, so it doesn't need to be virtual. –  Jan 14 '13 at 17:47
  • thank you. this is why I started this topic - to wrap up all those *little* details in one place – emesx Jan 14 '13 at 17:49