5

I have a base class called animal, and a dog and a cat that inherit from Animal. And a multiinheritance class called dogcat, that inherit from dog and cat, in Animal i have method called sleep. When i want to use that method from dogcat, i get the error "DogCat::sleep" is ambiguous, i do understand the problem, but i read in a book that it should be possible, when you declare sleep as virtual - but it does not work.

Is this not possible is the book wrong or is there any workaround?

class Animal
{
public:
    Animal(){}

    virtual void sleep()
    {
        cout << "zzzzzzzzz" << endl;
    }
    virtual void eat() = 0;

};

class Dog: public Animal
{
protected:
    Dog(){}

    virtual void eat() override
    {
        cout << "eats dogfood" << endl;
    } 
};

class Cat :public Animal
{
public:
    Cat(){}
    virtual void eat() override
    {
        cout << "eats catfood" << endl;
    }
};

class DogCat : public Dog, public Cat
{
public:
    DogCat(){}
    using Dog::eat;

};

int main(int argc, char** argv) {
    DogCat *DC = new DogCat();
    DC->sleep();//Error
}
Niklas
  • 1,753
  • 4
  • 16
  • 35

2 Answers2

6

You have the diamond problem

enter image description here

The "diamond problem" (sometimes referred to as the "deadly diamond of death"[4]) is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C. If there is a method in A that B and C have overridden, and D does not override it, then which version of the method does D inherit: that of B, or that of C?

So. Now you have two instances of A. What is it the solution? You have two:

  1. Define sleep operation in one of subclases and call it:
class Cat :public Animal
{
    public:
        Cat(){}
        virtual void eat() override
        {
            cout << "eats catfood" << endl;
        }

        void sleep()
        {
            Animal::sleep();
        }
};

int main(int argc, char** argv) {
    DogCat *DC = new DogCat();
    DC->Cat::sleep();
}
  1. Use virtual inheritance like says @Asesh answer. The problem is the common method eat(). You have to override it.
amchacon
  • 1,891
  • 1
  • 18
  • 29
  • 2
    you can remove `Cat::sleep` but implement `DogCat::sleep` by calling `Cat::sleep` that will naturally call `Animal::sleep` by the `Cat` path. – Franck Oct 09 '16 at 12:25
3

You should use virtual inheritance

class Animal
{
public:
    Animal(){}

    virtual void sleep()
    {
        cout << "zzzzzzzzz" << endl;
    }
    virtual void eat() = 0;

};

class Dog: virtual public Animal
{
protected:
    Dog(){}

    virtual void eat() override
    {
        cout << "eats dogfood" << endl;
    } 
};

class Cat : virtual public Animal
{
public:
    Cat(){}
    virtual void eat() override
    {
        cout << "eats catfood" << endl;
    }
};

class DogCat : public Dog, public Cat
{
public:
    DogCat(){}
    using Dog::eat;

};

int main(int argc, char** argv) {
    DogCat *DC = new DogCat();
    DC->sleep();//Error
}
Asesh
  • 3,186
  • 2
  • 21
  • 31
  • 2
    That's the right answer. However, it would be nice to elaborate a little further for OP to understand the diamond problem and why he has the ambiguity (i.e.multiple animal instances) and how your virtual inheritance solution solves this. – Christophe Oct 09 '16 at 12:18
  • @Christophe This still doesn't compile: http://coliru.stacked-crooked.com/a/07dafb58241bc191 – xinaiz Oct 09 '16 at 12:20
  • It works for sleep but i had to override eat in DogCat to get it to work, it said,override of virtual function "Animal::eat" is ambiguous and ambiguous inheritance of 'void Animal::eat(void)' – Niklas Oct 09 '16 at 12:20
  • 1
    @BlackMoses I just looked at the answer but didn't review the code. Indeed, the `use Dog::eat;` is just an alias. In the original OP code, both `eat()` exist, and this line just tell which one to call if no explicit scope resolution is used. This shortcut doesn't work with virtual inheritance as you have not only to tell which is the visible alias, but to chose the only and sole one to be implemented in the unique virtual base, i.e. `virtual void eat() override { Dog::eat(); }` as Niklas already found out ;-) – Christophe Oct 09 '16 at 12:32