2008-10-31 5 views
22

У меня есть абстрактный базовый класс, который действует как интерфейс.Множественное наследование от двух производных классов

У меня есть два «набора» производных классов, которые реализуют половину абстрактного класса. (один «набор» определяет абстрактные виртуальные методы, связанные с инициализацией, другой «набор» определяет те, которые связаны с фактической «работой».)

Затем я получил производные классы, которые используют множественное наследование для создания полностью определенных классов (и ничего не добавляет).

Итак: (плохо псевдокод)

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 как чистый интерфейс, этого можно было бы избежать (предполагая, что верный пример действителен).

So: - Я в порядке с моей реализацией? - Является ли это ограничением идиомы для создания виртуальных методов? - Как мне реорганизовать мой код, чтобы делать то, что я хочу? (Обеспечить один общий интерфейс, но позволить способ поменять реализации для «наборов» функций-членов)

Edit:

Кажется, я не первый: http://en.wikipedia.org/wiki/Diamond_problem

Кажется Virtual Наследование решение здесь. Раньше я слышал о виртуальном наследовании, но я не обворачивал его. Я по-прежнему открыт для предложений.

ответ

33

Похоже, вы хотите сделать виртуальное наследование. Независимо от того, что, оказывается, на самом деле быть хорошая идея, это другой вопрос, но вот как это сделать:


class AbsBase {...}; 
class AbsInit: public virtual AbsBase {...}; 
class AbsWork: public virtual AbsBase {...}; 
class NotAbsTotal: public AbsInit, public AbsWork {...}; 

В основном, по умолчанию, невиртуальное множественное наследование будет включать в себя копию каждый базовый класс в производный класс и включает все их методы. Вот почему у вас есть две копии AbsBase - и причина, по которой ваш метод используется, неоднозначна, загружаются оба набора методов, поэтому C++ не имеет способа узнать, какая копия для доступа!

Виртуальное наследование конденсирует все ссылки на виртуальный базовый класс в одну структуру данных. Это должно снова сделать методы из базового класса однозначными. Однако обратите внимание: если в двух промежуточных классах есть дополнительные данные, может возникнуть небольшая дополнительная накладная часть времени выполнения, чтобы код мог найти общий виртуальный базовый класс.

1

Это можно сделать, хотя это и дает большую часть дрожь.

Вы должны использовать «виртуальное наследование», синтаксис, который является чем-то вроде

class AbsInit: public virtual AbsBase {...}; 
class AbsWork: public virtual AbsBase {...}; 
class NotAbsTotal: public AbsInit, public AbsWork {...}; 

Затем вы должны указать, какие функции вы хотите использовать:

NotAbsTotal::work() 
{ 
    AbsInit::work_impl(); 
} 

(ОБНОВЛЕНО с правильным синтаксис)

1

Вы должны объявить наследование как виртуальные:

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; 
+0

Это работает хорошо, даже с моими невиртуальными общественными методами. Тем не менее, мне нужно попытаться выяснить, что происходит при использовании указателя типа Base для объекта типа Derived, чтобы убедиться, что виртуальное наследование работает как перегрузка виртуальных функций :) – mmocny 2008-10-31 20:21:38

+0

При преобразовании из виртуальной базы в производный класс вы всегда нужно использовать dynamic_cast <>, что, в свою очередь, требует, чтобы виртуальный метод был определен в виртуальной базе.Простое использование виртуальной базы не должно вызывать проблем - оно будет полиморфно предоставлять доступ к производному классу. – 2008-10-31 23:29:10

0

Вы должны начать думать в терминах того, что вы пытаетесь моделировать здесь.

Публичное наследование должно использоваться только для моделирования отношения «isa», например. собака - животное, квадрат - это фигура и т. д.

Посмотрите на книгу Скотта Мейера «Эффективный C++» для отличного эссе о том, какие аспекты дизайна OO следует только интерпретировать.

Редактировать: Я забыл сказать, что хотя ответы, предоставленные до сих пор, технически правильны, я не думаю, что любой из них затрагивает проблемы того, что вы пытаетесь моделировать, и это суть вашей проблемы!

НТН

веселит,

Роб

0

я нашел хороший и простой пример на ссылке ниже. В статье объясняется примерная программа для расчета площади и периметра прямоугольника. Вы можете это проверить .. cheers

Многоуровневое наследование - это иерархия наследования, в которой один производный класс наследуется от нескольких базовых классов. Подробнее ..

http://www.mobihackman.in/2013/09/multiple-inheritance-example.html

Смежные вопросы