Множественное наследование от двух производных классов
У меня есть абстрактный базовый класс, который действует как интерфейс.
У меня есть два "набора" производных классов, которые реализуют половину абстрактного класса. (один "набор" определяет абстрактные виртуальные методы, связанные с инициализацией, другой "набор" определяет те, которые связаны с фактической "работой".)
Затем я получил производные классы, которые используют множественное наследование для создания полностью определенных классов (и сам ничего не добавляет).
Итак: (плохой псевдокод)
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 как чистый интерфейс, этого бы избежать (предполагая, что верхний пример верен).
Итак: - Я далеко от моей реализации? - Является ли это ограничением идиомы создания виртуальных методов частными? - Как мне реорганизовать мой код, чтобы делать то, что я хочу? (Обеспечьте один общий интерфейс, но разрешите способ поменять местами реализации для "наборов" функций-членов)
Редактировать:
Кажется, я не первый: http://en.wikipedia.org/wiki/Diamond_problem
Кажется, Виртуальное Наследование является решением здесь. Я слышал о виртуальном наследовании и раньше, но я не обернулся вокруг него. Я все еще открыт для предложений.
5 ответов
Похоже, вы хотите сделать виртуальное наследование. Является ли это действительно хорошей идеей, это другой вопрос, но вот как вы это делаете:
class AbsBase {...};
class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};
По сути, не виртуальное множественное наследование по умолчанию будет включать в себя копию каждого базового класса в производном классе и включает в себя все их методы. Вот почему у вас есть две копии AbsBase - и причина, по которой ваш метод неоднозначен, заключается в том, что оба набора методов загружены, поэтому у C++ нет способа узнать, к какой копии обращаться!
Виртуальное наследование объединяет все ссылки на виртуальный базовый класс в одну структуру данных. Это должно сделать методы из базового класса снова однозначными. Однако обратите внимание: если в двух промежуточных классах есть дополнительные данные, могут возникнуть небольшие дополнительные накладные расходы времени выполнения, чтобы код мог найти общий виртуальный базовый класс.
Это можно сделать, хотя это больше всего вызывает дрожь.
Вам нужно использовать "виртуальное наследование", синтаксис которого для чего-то вроде
class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};
Затем вы должны указать, какую функцию вы хотите использовать:
NotAbsTotal::work()
{
AbsInit::work_impl();
}
(ОБНОВЛЕН с правильным синтаксисом)
Вам необходимо объявить наследование виртуальным:
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;
Вы должны начать думать с точки зрения того, что вы пытаетесь смоделировать здесь.
Публичное наследование должно использоваться только для моделирования отношений "isa", например, собака - это животное, квадрат - это форма и т. Д.
Взгляните на книгу Скотта Мейера Effective C++, где вы найдете превосходное эссе о том, как различные аспекты ОО-дизайна следует интерпретировать.
Редактировать: я забыл сказать, что, хотя представленные ответы являются технически правильными, я не думаю, что какие-либо из них касаются вопросов того, что вы пытаетесь смоделировать, и это суть вашей проблемы!
НТН
веселит,
обкрадывать
Я нашел хороший и простой пример по ссылке ниже. Статья поясняется на примере программы для расчета площади и периметра прямоугольника. Вы можете проверить это.. ура
Многоуровневое наследование - это иерархия наследования, в которой один производный класс наследуется от нескольких базовых классов. Прочитайте больше..
http://www.mobihackman.in/2013/09/multiple-inheritance-example.html