Множественное наследование, С++ и та же подпись метода в нескольких суперклассах
У меня нет опыта работы на С++, и я исхожу из фона Java. В последнее время меня спросили в интервью, почему Java не допускает множественного наследования, и ответ был довольно прост. Тем не менее, мне все еще интересно, как С++ имеет дело с этим, поскольку он позволяет наследовать более чем один класс.
В частности, скажем, что существует класс под названием MechanicalEngineer
, а другой - ElectricalEngineer
. Оба имеют метод под названием buildRobot()
.
Что произойдет, если мы создадим третий класс RoboticsEngineer
, который будет удаляться из обоих и не переопределяет этот метод, и вы просто вызываете:
(some instance of RoboticsEngineer).buildRobot()
Будет ли выбрано исключение, или будет использоваться метод из одного из суперклассов? Если да, то каким образом компилятор знает, какой класс использовать?
Ответы
Ответ 1
Компилятор будет отмечать такую ситуацию (т.е. пытаться вызвать (some instance of RoboticsEngineer).buildRobot()
) как ошибку.
Это происходит из-за того, что производный объект получил копию обоих базовых объектов (экземпляр 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();
в качестве ошибки.
Ответ 2
Он не справляется с этим. Это двусмысленно. error C2385: ambiguous access of 'functionName'
Компилятор достаточно умен, чтобы знать, что он не должен угадывать ваш смысл.
Для компиляции программы вам необходимо сообщить компилятору, что:
О. Вы знаете, что это проблема.
B. Расскажите, что именно вы имеете в виду.
Для этого вам нужно явно указать компилятору, какой метод вы запрашиваете:
RoboticsEngineer myRobot;
myRobot.ElectricalEngineer::buildRobot();
Ответ 3
Компилятор будет жаловаться на подобную ситуацию.
В таком случае С++ предлагает создать интерфейс с функцией "pure virtual" метода buildRobot(). MechanicalEngineer и EletricalEnginner наследуют интерфейс и переопределяют функцию buildRoboot().
Когда вы создаете объект RoboticsEnginner и вызываете функцию buildRobot(), вызывается функция интерфейса.
Ответ 4
struct MecEngineer {
void buildRobot() { /* .... */ }
};
struct EleEngineer {
void buildRobot() { /* .... */ }
};
struct RoboticsEngineer : MecEngineer, EleEngineer {
};
Теперь, когда вы это сделаете,
robEngObject -> buildRobot() ;
Такой вызов не может быть разрешен и неоднозначен, потому что оба под-объекта имеют функцию-член с одной и той же сигнатурой, а компилятор не знает, какой из них вызывать. В такой ситуации вам нужно явно указать его с помощью оператора ::
или с помощью static_cast
.
static_cast<MecEngineer*> (robEngObject) -> buildRobot() ;
Ответ 5
Нет ничего о множественном наследовании класса, которое позволяет это. Java создает ту же проблему с интерфейсами.
public interface A {
public void doStuff();
}
public interface B {
public void doStuff();
}
public class C implements A, B {}
Простой ответ заключается в том, что компилятор выдает ему ошибку.