Множественное наследование, C ++ и одна и та же сигнатура метода в нескольких суперклассах - PullRequest
23 голосов
/ 20 июля 2011

У меня нет опыта работы с C ++, и я пришел из Java. В последнее время меня спросили в интервью, почему Java не допускает множественное наследование, и ответ был довольно простым. Однако мне все еще интересно, как C ++ справляется с этим, поскольку он позволяет вам наследовать от более чем одного класса.

В частности, скажем, есть класс с именем MechanicalEngineer, а другой называется ElectricalEngineer. Оба имеют метод с именем buildRobot().

Что произойдет, если мы создадим третий класс RoboticsEngineer, который наследует от обоих и не переопределяет этот метод, а вы просто вызываете:

(some instance of RoboticsEngineer).buildRobot()

Будет ли выдано исключение или будет использован метод одного из суперклассов? Если да, то как компилятор узнает, какой класс использовать?

Ответы [ 5 ]

22 голосов
/ 20 июля 2011

Компилятор помечает ситуацию такого типа (т. Е. Пытается вызвать (some instance of RoboticsEngineer).buildRobot()) как ошибку.

Это происходит потому, что производный объект получил копию обоих базовых объектов (a MechanicalEngineerэкземпляр и ElectricalEngineer экземпляр) внутри себя и одной только сигнатуры метода недостаточно, чтобы указать, какой из них использовать.

Если вы переопределите buildRobot в своем RoboticsEngineer, вы сможете сказатьявно какой унаследованный метод использовать с префиксом имени класса, например:

void RoboticsEngineer::buildRobot() {
    ElectricalEngineer::buildRobot()
}

Одной и той же монетой можно фактически «заставить» компилятор использовать ту или иную версию buildRobot, добавив к ней префиксимя класса:

 (some instance of RoboticsEngineer).ElectricalEngineer::buildRobot();

в этом случае будет вызвана реализация метода ElectricalEngineer, без двусмысленности.

Особый случай дается, когда у вас есть база Engineerприсвойте MechanicalEngineer и ElectricalEngineer, и вы укажете наследование virtual в обоих случаях.Когда используется virtual, производный объект не содержит двух экземпляров Engineer, но компилятор обеспечивает наличие только одного из них.Это будет выглядеть так:

 class Engineer {
      void buildRobot();
 };

 class MechanicalEngineer: public virtual Engineer {

 };

 class ElectricalEngineer: public virtual Engineer {

 };

В этом случае

(some instance of RoboticsEngineer).buildRobot();

будет разрешено без двусмысленностей.То же самое верно, если buildRobot объявлен virtual и переопределен в одном из двух производных классов.В любом случае, если оба производных класса (ElectricalEngineer и MechanicalEngineer) переопределяют buildRobot, тогда возникает неоднозначность, и компилятор помечает попытку вызова (some instance of RoboticsEngineer).buildRobot(); как ошибку.

5 голосов
/ 20 июля 2011

Это не справится. Это неоднозначно. error C2385: ambiguous access of 'functionName'

Компилятор достаточно умен, чтобы знать, что он не должен угадывать ваше значение.
Для компиляции программы необходимо указать компилятору, что:
А. Вы знаете, что это проблематичный вопрос.
Б. Скажи, что именно ты имеешь в виду.

Для этого вам нужно явно указать компилятору, какой метод вы запрашиваете:

RoboticsEngineer myRobot;
myRobot.ElectricalEngineer::buildRobot();
0 голосов
/ 20 июля 2011

Нет ничего о множественном наследовании классов, которое позволяет это. Java создает ту же проблему с интерфейсами.

public interface A {
    public void doStuff();
}
public interface B {
    public void doStuff();
}
public class C implements A, B {}

Простой ответ заключается в том, что компилятор выдает ошибку.

0 голосов
/ 20 июля 2011
struct MecEngineer {

     void buildRobot() { /* .... */ }

};

struct EleEngineer {

     void buildRobot() { /* .... */ }

};

struct RoboticsEngineer : MecEngineer, EleEngineer {

};

Теперь, когда вы это сделаете,

robEngObject -> buildRobot() ;

Такой вызов не может быть разрешен и является неоднозначным, поскольку оба подобъекта имеют функцию-член с одинаковой сигнатурой, а компилятор не знает, какой из них вызывать. В такой ситуации вам нужно явно указать это с помощью оператора :: или с помощью static_cast.

static_cast<MecEngineer*> (robEngObject) -> buildRobot() ;
0 голосов
/ 20 июля 2011

Компилятор будет жаловаться на такую ​​ситуацию.

В таком случае c ++ предлагает создать интерфейс с функцией «чисто виртуального» метода buildRobot ().MechanicalEngineer и EletricalEnginner наследуют интерфейс и переопределяют функцию buildRoboot ().

Когда вы создаете объект RoboticsEnginner и вызываете функцию buildRobot (), вызывается функция интерфейса.

...