How do you declare an interface in C++?

2018-12-31 06:13发布

How do I setup a class that represents an interface? Is this just an abstract base class?

15条回答
临风纵饮
2楼-- · 2018-12-31 06:33

There is no concept of "interface" per se in C++. AFAIK, interfaces were first introduced in Java to work around the lack of multiple inheritance. This concept has turned out to be quite useful, and the same effect can be achieved in C++ by using an abstract base class.

An abstract base class is a class in which at least one member function (method in Java lingo) is a pure virtual function declared using the following syntax:

class A
{
  virtual void foo() = 0;
};

An abstract base class cannot be instantiated, i. e. you cannot declare an object of class A. You can only derive classes from A, but any derived class that does not provide an implementation of foo() will also be abstract. In order to stop being abstract, a derived class must provide implementations for all pure virtual functions it inherits.

Note that an abstract base class can be more than an interface, because it can contain data members and member functions that are not pure virtual. An equivalent of an interface would be an abstract base class without any data with only pure virtual functions.

And, as Mark Ransom pointed out, an abstract base class should provide a virtual destructor, just like any base class, for that matter.

查看更多
几人难应
3楼-- · 2018-12-31 06:33

You can also consider contract classes implemented with the NVI (Non Virtual Interface Pattern). For instance:

struct Contract1 : boost::noncopyable
{
    virtual ~Contract1();
    void f(Parameters p) {
        assert(checkFPreconditions(p)&&"Contract1::f, pre-condition failure");
        // + class invariants.
        do_f(p);
        // Check post-conditions + class invariants.
    }
private:
    virtual void do_f(Parameters p) = 0;
};
...
class Concrete : public Contract1, public Contract2
{
private:
    virtual void do_f(Parameters p); // From contract 1.
    virtual void do_g(Parameters p); // From contract 2.
};
查看更多
看淡一切
4楼-- · 2018-12-31 06:35

All good answers above. One extra thing you should keep in mind - you can also have a pure virtual destructor. The only difference is that you still need to implement it.

Confused?


    --- header file ----
    class foo {
    public:
      foo() {;}
      virtual ~foo() = 0;

      virtual bool overrideMe() {return false;}
    };

    ---- source ----
    foo::~foo()
    {
    }

The main reason you'd want to do this is if you want to provide interface methods, as I have, but make overriding them optional.

To make the class an interface class requires a pure virtual method, but all of your virtual methods have default implementations, so the only method left to make pure virtual is the destructor.

Reimplementing a destructor in the derived class is no big deal at all - I always reimplement a destructor, virtual or not, in my derived classes.

查看更多
后来的你喜欢了谁
5楼-- · 2018-12-31 06:39

A little addition to what's written up there:

First, make sure your destructor is also pure virtual

Second, you may want to inherit virtually (rather than normally) when you do implement, just for good measures.

查看更多
笑指拈花
6楼-- · 2018-12-31 06:41

In C++11 you can easily avoid inheritance altogether:

struct Interface {
  explicit Interface(SomeType& other)
  : foo([=](){ return other.my_foo(); }), 
    bar([=](){ return other.my_bar(); }), /*...*/ {}
  explicit Interface(SomeOtherType& other)
  : foo([=](){ return other.some_foo(); }), 
    bar([=](){ return other.some_bar(); }), /*...*/ {}
  // you can add more types here...

  // or use a generic constructor:
  template<class T>
  explicit Interface(T& other)
  : foo([=](){ return other.foo(); }), 
    bar([=](){ return other.bar(); }), /*...*/ {}

  const std::function<void(std::string)> foo;
  const std::function<void(std::string)> bar;
  // ...
};

In this case, an Interface has reference semantics, i.e. you have to make sure that the object outlives the interface (it is also possible to make interfaces with value semantics).

These type of interfaces have their pros and cons:

  • They require more memory than inheritance based polymorphism.
  • They are in general faster than inheritance based polymorphism.
  • In those cases in which you know the final type, they are much faster! (some compilers like gcc and clang perform more optimizations in types that do not have/inherit from types with virtual functions).

Finally, inheritance is the root of all evil in complex software design. In Sean Parent's Value Semantics and Concepts-based Polymorphism (highly recommended, better versions of this technique are explained there) the following case is studied:

Say I have an application in which I deal with my shapes polymorphically using the MyShape interface:

struct MyShape { virtual void my_draw() = 0; };
struct Circle : MyShape { void my_draw() { /* ... */ } };
// more shapes: e.g. triangle

In your application, you do the same with different shapes using the YourShape interface:

struct YourShape { virtual void your_draw() = 0; };
struct Square : YourShape { void your_draw() { /* ... */ } };
/// some more shapes here...

Now say you want to use some of the shapes that I've developed in your application. Conceptually, our shapes have the same interface, but to make my shapes work in your application you would need to extend my shapes as follows:

struct Circle : MyShape, YourShape { 
  void my_draw() { /*stays the same*/ };
  void your_draw() { my_draw(); }
};

First, modifying my shapes might not be possible at all. Furthermore, multiple inheritance leads the road to spaghetti code (imagine a third project comes in that is using the TheirShape interface... what happens if they also call their draw function my_draw ?).

Update: There are a couple of new references about non-inheritance based polymorphism:

查看更多
深知你不懂我心
7楼-- · 2018-12-31 06:45

While it's true that virtual is the de-facto standard to define an interface, let's not forget about the classic C-like pattern, which comes with a constructor in C++:

struct IButton
{
    void (*click)(); // might be std::function(void()) if you prefer

    IButton( void (*click_)() )
    : click(click_)
    {
    }
};

// call as:
// (button.*click)();

This has the advantage that you can re-bind events runtime without having to construct your class again (as C++ does not have a syntax for changing polymorphic types, this is a workaround for chameleon classes).

Tips:

  • You might inherit from this as a base class (both virtual and non-virtual are permitted) and fill click in your descendant's constructor.
  • You might have the function pointer as a protected member and have a public reference and/or getter.
  • As mentioned above, this allows you to switch the implementation in runtime. Thus it's a way to manage state as well. Depending on the number of ifs vs. state changes in your code, this might be faster than switch()es or ifs (turnaround is expected around 3-4 ifs, but always measure first.
  • If you choose std::function<> over function pointers, you might be able to manage all your object data within IBase. From this point, you can have value schematics for IBase (e.g., std::vector<IBase> will work). Note that this might be slower depending on your compiler and STL code; also that current implementations of std::function<> tend to have an overhead when compared to function pointers or even virtual functions (this might change in the future).
查看更多
登录 后发表回答