0

I have a specific class structure in mind, though I feel like it could become a little messy when the project gets bigger. The general idea is the following:

There is a class for all possible vehicles with basic implementations that do not change for any vehicle, regardless of its specific type. Then, for all the cars, there should be an interface with functions only cars can have. This interface is purely virtual though. Then, any car can implement these functions as needed. I included a very simplistic code example below:

class Vehicle { //general class for a vehicle
public:
    void move() {
        std::cout << "I moved\n"; 
    }
    void crash() {
        std::cout << "I crashed\n"
    }
};

class CarInterface : public Vehicle { //purely virtual interface for cars
public:
    virtual void refuel() = 0;
};

class Ferrari: public CarInterface { //implementation: one specific car
public:
    void refuel() override {
        std::cout << "I use ferrari fuel\n"
    };
};

In the c++ guidelines, it says you should distinguish between implementation inheritance and interface inheritance. Does this still fulfill that requirement?

Eikeike
  • 23
  • 5
  • 1
    `CarInterface` is *not* a purely virtual interface when it inherits from `Vehicle` – 463035818_is_not_an_ai Feb 15 '21 at 14:02
  • 1
    Yes, an abstract class can inherit from a non-abstract class, but it does not have a purely virtual interface. However, doing so means you breach the requirement for distinguishing between implementation inheritance and interface inheritance (since classes derived from your abstract class inherit both implementation and interface). – Peter Feb 15 '21 at 14:07
  • `though I feel like it could become a little messy when the project gets bigger.` This cannot really be answered in the scope of StackOverflow. Inheritance always comes with the problem that you might not be able to create a really consistent and general-purpose interface. That way many c++ libraries try to keep inheritance at a minimum. There are various other design patterns like composition, that might be a better choice. – t.niese Feb 15 '21 at 14:11
  • can you clarify what part of the c++ guidelines you are worried about specifically? Note that the guidelines are not hard requirements. They are just guidelines, written by some guys with lots of experience, but still "just" guidelines – 463035818_is_not_an_ai Feb 15 '21 at 14:17
  • @largest_prime_is_463035818 I was referring to [this link](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c129-when-designing-a-class-hierarchy-distinguish-between-implementation-inheritance-and-interface-inheritance). But knowing that it cannot be considered purely virtual already clears things up a little – Eikeike Feb 15 '21 at 14:22
  • your example is similar to their "bad" example of the `Shape`, you only moved both parts into seperate classes, but in `CarInterface` interface and implementation are mixed – 463035818_is_not_an_ai Feb 15 '21 at 14:25
  • I might go with composition [Demo](http://coliru.stacked-crooked.com/a/2a1c93cdcbca1525). – Jarod42 Feb 16 '21 at 13:29

1 Answers1

3

Can a C++ interface inherit from a class with full implementation?

The C++ language doesn't distinguish between interfaces and other classes. The distinction is purely conventional. Thus from one perspective it would be correct to say that C++ interface cannot [whatever] because there is no such thing as C++ interface.

If we consider an interface as an abstract concept, then the answer depends on how one chooses to interpret the concept, and to precisely specify it in terms of the language. If the interpretation is that an interface class has only pure virtual member functions (and this is a common interpretation), then CarInterface is not an interface because it has non-pure-virtual member functions through inheritance.


In the c++ guidelines

I suppose that you intend to refer to the "C++ Core Guidelines". This is how the concepts are defined there:

Note Definition:

  • interface inheritance is the use of inheritance to separate users from implementations, in particular to allow derived classes to be added and changed without affecting the users of base classes.
  • implementation inheritance is the use of inheritance to simplify implementation of new facilities by making useful operations available for implementers of related new operations (sometimes called “programming by difference”).

A pure interface class is simply a set of pure virtual functions; see I.25.

This is a little bit vague, but I would interpret that CarInterface is a form of both interface inheritance, and implementation inheritance, and it is not a pure interface.

Below the definitions, as well as in section I.25, the guidelines demonstrate such mixed inheritance as a bad example, and suggests an alternative that uses a pure interface instead.


Your example could achieve both pure interfaces, and reuse (which is what your Vehicle class presumably is for) by using virtual inheritance:

// pure interface
class VehicleInterface {
public:
    virtual void move() = 0;
    virtual void crash() = 0;
};

// pure interface
class CarInterface : public virtual VehicleInterface {
public:
    virtual void refuel() = 0;
};

class CommonVehicle : public virtual VehicleInterface {
public:
    void move() override;
    void crash() override;
};

class Ferrari: public CarInterface, private CommonVehicle {
public:
    void refuel() override;
};
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Thanks a lot, I didn't know it's not considered a pure interface then! This is a question nobody can really answer, but would you consider it a bad practice to keep this class structure? I thought if it works for me it doesn't really matter if it's a bad practice or not, but I don't want my whole codebase to be based on a bad idea – Eikeike Feb 15 '21 at 14:25
  • @Eikeike I added an example of how to use pure inheritance in your example. – eerorika Feb 15 '21 at 14:40
  • @Eikeike: You have to understand the reasons of the guideline. Alternatives might be worst (I don't see real gain to add virtual `move`/`crash` just to satisfy the guideline for example). In your toy example, methods can be free functions, so harder to give correct alternative. In linked guideline, interface itself is strange. Interfaces tend to be more stable than implementation (so when you mix, you generally got the cons of both). – Jarod42 Feb 15 '21 at 15:22