c++ – C ++ Inheriting Interfaces and Implementing Classes

Question:

Let's say I have a cat, which, of course, is an animal:

class Animal {
public:
    void walk() { std::cout << "walking..."; }
    virtual void say() const = 0;
};

class Cat : public Animal {
public:
    void say() const override { std::cout << "meow"; }
    void purr() const override { std::cout << "purr..."; }
};

And my cat must also be able to walk between worlds. That is, modules. And for this, it must have an ICat interface, through which external modules use it. At the same time, external modules may not know how to work with cats, but be able to work with animals. So the IAnimal interface is also needed. As a former C # developer, I immediately built the following system:

struct IAnimal {
public:
    virtual ~IAnimal() = default;
    virtual void walk() = 0;
    virtual void say() const = 0;
};

struct ICat : public virtual IAnimal {
public:
    virtual void purr() const = 0;
};

class Animal : public virtual IAnimal {
public:
    void walk() override { std::cout << "walking..."; }
};

class Cat : public Animal, public ICat {
public:
    void say() const override { std::cout << "meow"; }
    void purr() const override { std::cout << "purr..."; }
};

There was no limit to my disappointment when I saw a warning in response:

warning C4250: 'Cat' : inherits 'Animal::Animal::walk' via dominance.

The first option that comes to mind: since the compiler simply does not understand that I will never start redefining walk in ICat (after all, ICat is just a weak-willed interface), you can simply put a #pragma warning. And that's all.

Another option is to remove the inheritance of ICat from IAnimal. This will remove the warning, but then you have to do infinite ICat castes to IAnimal. At the same time, looking at the description of the ICat interface available from the outside, it is not at all obvious that it can be converted to IAnimal. And yes, now this is obvious from the concepts of a cat and an animal. In a real situation, with complex hierarchies, such clarity cannot be expected.

You can "tell" about the presence of such a caste by adding the IAnimal ICat :: asAnimal () method. This solves most of the questions, but it is very far from C # -powered simplicity and clarity.

Actually the question is: how to do it correctly in C ++?

Answer:

The point is that inheritance in C ++ is not the same as in C #. In particular, the concept of "interface" is not there at all, and you had to emulate it using multiple inheritance.

Thus, you really have walk inherited in two ways. In the case when one of the ancestor classes (in your case ICat ) does not overlap walk , the compiler can "independently" choose the implementation in Cat . But nevertheless, the Visual Studio compiler honestly warns about possible problems (about them here and a little here ), which lie not in the plane of the code, but in the plane of "expected" behavior. In your case (interface emulation) these problems will not arise.

So just suppress this warning (via #pragma warning ) and program further.


By the way, gcc compiles your code without warnings.

Scroll to Top