Множественное наследование из двух производных классов
У меня есть абстрактный базовый класс, который действует как интерфейс.
У меня есть два "набора" производных классов, которые реализуют половину абстрактного класса. (один "набор" определяет абстрактные виртуальные методы, связанные с инициализацией, другой "набор" определяет те, которые связаны с фактической "работой".)
Затем я получил производные классы, которые используют множественное наследование для создания полностью определенных классов (и ничего не добавляет).
Итак: (плохой псевдокод)
class AbsBase {
virtual void init() = 0;
virtual void work() = 0;
}
class AbsInit : public AbsBase {
void init() { do_this(); }
// work() still abs
}
class AbsWork : public AbsBase {
void work() { do_this(); }
// init() still abs
}
class NotAbsTotal : public AbsInit, public AbsWork {
// Nothing, both should be defined
}
Прежде всего, могу ли я это сделать? Могу ли я наследовать от двух классов, которые оба получены из одной базы? (Надеюсь, что так).
Вот "настоящая проблема", хотя (я немного соврал, чтобы упростить пример).
То, что я действительно сделал и сделал, добавляет методы абстрактного доступа к базовому классу:
class AbsBase {
public:
void init() { init_impl(); }
void work() { work_impl(); }
private:
virtual void init_impl() = 0;
virtual void work_impl() = 0;
}
Потому что общая идиома состоит в том, чтобы сделать все виртуальные методы частными.
К сожалению, теперь оба AbsInit и AbsWork наследуют эти методы, и поэтому NotAbsTotal наследует "два из каждого" (я понимаю, что я могу разбить то, что действительно происходит во время компиляции).
В любом случае g++ жалуется, что: "запрос для члена init() неоднозначен" при попытке использовать класс.
Я предполагаю, что если бы я использовал свой класс AbsBase как чистый интерфейс, этого можно было бы избежать (предполагая, что верхний пример действителен).
Итак:
- У меня с моей реализацией?
- Является ли это ограничением идиомы для создания виртуальных методов?
- Как мне реорганизовать мой код, чтобы делать то, что я хочу? (Предоставьте один общий интерфейс, но дайте возможность поменять местами реализации для "наборов" функций-членов)
Edit:
Кажется, я не первый:
http://en.wikipedia.org/wiki/Diamond_problem
Кажется, что Virtual Inheritance является решением. Раньше я слышал о виртуальном наследовании, но я не обворачивал его. Я по-прежнему открыт для предложений.
Ответы
Ответ 1
Похоже, вы хотите сделать виртуальное наследование. На самом деле это хорошая идея, это другой вопрос, но вот как вы это делаете:
class AbsBase {...};
class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};
В принципе, не виртуальное множественное наследование по умолчанию будет включать в себя копию каждого базового класса в производном классе и включает все их методы. Вот почему у вас есть две копии AbsBase - и причина, по которой ваш метод используется, неоднозначен, загружаются оба набора методов, поэтому С++ не имеет возможности узнать, какая копия для доступа!
Виртуальное наследование конденсирует все ссылки на виртуальный базовый класс в одну структуру данных. Это должно снова сделать методы из базового класса однозначными. Однако обратите внимание: если в двух промежуточных классах есть дополнительные данные, может возникнуть небольшая дополнительная накладная часть времени выполнения, чтобы код мог найти общий виртуальный базовый класс.
Ответ 2
Это можно сделать, хотя это дает большую часть дрожь.
Вам нужно использовать "виртуальное наследование", синтаксис, для которого что-то вроде
class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};
Затем вы должны указать, какую функцию вы хотите использовать:
NotAbsTotal::work()
{
AbsInit::work_impl();
}
(ОБНОВЛЕНО с правильным синтаксисом)
Ответ 3
Вам нужно объявить наследование виртуальным:
struct AbsBase {
virtual void init() = 0;
virtual void work() = 0;
};
struct AbsInit : virtual public AbsBase {
void init() { }
};
struct AbsWork : virtual public AbsBase {
void work() { }
};
struct NotAbsTotal : virtual public AbsInit, virtual public AbsWork {
};
void f(NotAbsTotal *p)
{
p->init();
}
NotAbsTotal x;
Ответ 4
Вы должны начать думать в терминах того, что вы пытаетесь моделировать здесь.
Публичное наследование должно использоваться только для моделирования отношения "isa", например. собака - животное, квадрат - форма и т.д.
Посмотрите на книгу Скотта Мейера "Эффективный С++" для отличного эссе о том, что различные аспекты дизайна OO следует интерпретировать только как.
Изменить: я забыл сказать, что, хотя предоставленные до сих пор ответы являются технически правильными, я не думаю, что любой из них затрагивает проблемы того, что вы пытаетесь моделировать, и в этом суть вашей проблемы!
НТН
веселит,
Rob
Ответ 5
Я нашел хороший и простой пример в приведенной ниже ссылке. В статье объясняется примерная программа для расчета площади и периметра прямоугольника. Вы можете проверить его. Cheers
Многоуровневое наследование - это иерархия наследования, в которой один производный класс наследуется от нескольких базовых классов. Подробнее..
http://www.mobihackman.in/2013/09/multiple-inheritance-example.html