#c #multiple-inheritance
#c #множественное наследование
Вопрос:
У меня есть базовый класс с именем animal, а также dog и cat, которые наследуются от Animal. И класс множественного наследования, называемый dogcat, который наследуется от dog и cat, в Animal у меня есть метод, называемый sleep . Когда я хочу использовать этот метод из dogcat, я получаю сообщение об ошибке «DogCat::sleep» неоднозначно, я понимаю проблему, но я прочитал в книге, что это должно быть возможно, когда вы объявляете sleep виртуальным — но это не работает.
Это невозможно, книга неверна или есть какой-либо обходной путь?
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
}
Комментарии:
1. Сколько видов животных, о которых вы знаете, являются кошками и собаками?
2. имхо, большинство случаев проблемы с бриллиантами указывают на плохой дизайн класса. И ваш случай определенно попадает в эту категорию.
Ответ №1:
У вас проблема с бриллиантами
«Алмазная проблема» (иногда называемая «смертоносным алмазом смерти»[4]) — это двусмысленность, возникающая, когда два класса B и C наследуют от A, а класс D наследует как от B, так и от C. Если в A есть метод, который переопределили B и C, а D не переопределяет его, то какую версию метода наследует D: B или C?
Итак. Теперь у вас есть два экземпляра A. Какое это решение? У вас есть два:
- Определите операцию ожидания в одном из подклассов и вызовите ее:
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();
}
- Используйте виртуальное наследование, как сказано в ответе @Asesh. Проблема заключается в распространенном методе eat() . Вы должны переопределить его.
Комментарии:
1. вы можете удалить
Cat::sleep
, но реализоватьDogCat::sleep
путем вызоваCat::sleep
, который, естественно, будет вызыватьсяAnimal::sleep
поCat
пути.
Ответ №2:
Вы должны использовать виртуальное наследование
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
}
Комментарии:
1. Это правильный ответ. Тем не менее, было бы неплохо немного подробнее рассказать OP, чтобы понять проблему diamond и почему у него есть двусмысленность (т.Е. Несколько экземпляров animal) и как ваше решение для виртуального наследования решает эту проблему.
2. @Christophe Это все еще не компилируется: coliru.stacked-crooked.com/a/07dafb58241bc191
3. Он работает для сна, но мне пришлось переопределить eat в DogCat, чтобы заставить его работать, в нем говорилось, что переопределение виртуальной функции «Animal:: eat» является неоднозначным и неоднозначным наследованием «void Animal:: eat (void)»
4. @BlackMoses Я просто посмотрел на ответ, но не просматривал код. Действительно,
use Dog::eat;
это просто псевдоним. В исходном операционном коде обаeat()
существуют, и эта строка просто указывает, какой из них вызывать, если не используется явное разрешение области. Этот ярлык не работает с виртуальным наследованием, поскольку вам нужно не только указать, какой видимый псевдоним, но и выбрать единственный и единственный, который будет реализован в уникальной виртуальной базе, т. Е.virtual void eat() override { Dog::eat(); }
, Как уже выяснил Никлас 😉