Множественное наследование: 2Classes1Method
Я только что пробовал этот фрагмент кода:
struct FaceOfPast
{
virtual void Smile() = 0;
};
struct FaceOfFuture
{
virtual void Smile() = 0;
};
struct Janus : public FaceOfPast, public FaceOfFuture
{
virtual void Smile() {printf(":) ");}
};
...
void main()
{
Janus* j = new Janus();
FaceOfFuture* future = j;
FaceOfPast* past = j;
future->Smile();
past->Smile();
delete j;
}
Он работает по назначению (выводит два смайлика), но я не думаю, что он должен даже компилироваться, а переопределение Smile()
в Janus
неоднозначно.
Как (и почему) это работает?
Ответы
Ответ 1
Нет никакой двусмысленности, потому что вы вызываете Smile()
на указатели на FaceOfFuture
и FaceOfPast
, которые объявляют только один метод Smile()
.
Поскольку вызов метода на указателе базового класса не может привести к двусмысленности, давайте рассмотрим ситуации, когда вы вызываете метод непосредственно по указателю дочернего класса:
Janus* j = new Janus();
j->Smile();
Производный класс, помимо переопределения, также скрывает объявление базовых классов Smile()
. У вас была бы двусмысленность, если бы вы не переопределили метод в производном классе:
Следующие компиляции:
struct FaceOfPast
{
virtual void Smile() {printf(":) ");}
};
struct FaceOfFuture
{
virtual void Smile() {printf(":) ");}
};
struct Janus : public FaceOfPast, public FaceOfFuture
{
virtual void Smile() {printf(":) ");}
};
int main()
{
Janus* j = new Janus();
j->Smile();
}
Хотя вы вызываете Smile
в Janus
, объявления базового класса скрыты.
Нельзя:
struct FaceOfPast
{
virtual void Smile() {printf(":) ");}
};
struct FaceOfFuture
{
virtual void Smile() {printf(":) ");}
};
struct Janus : public FaceOfPast, public FaceOfFuture
{
};
int main()
{
Janus* j = new Janus();
j->Smile();
}
Из-за двусмысленности.
Ответ 2
Согласно стандарту С++ (10.3.2):
Если виртуальная функция-член vf объявлена в классе Base и в классе Derived, полученном прямо или косвенно из Base, объявлена функция члена vf с тем же именем, списком параметров, cv-qualification и ref-qualifier (или отсутствием такого же), что и Base:: vf то Derived:: vf [...] переопределяет Base:: vf.
Как правило, для множественного наследования нет никакого специального лечения, поэтому он, скорее всего, применим и здесь: void Janus::Smile()
переопределяет оба метода без какой-либо двусмысленности, просто потому, что он имеет то же имя и подпись, что и методы базового класса.
Ответ 3
Janus* j = new Janus();
FaceOfFuture* future = j;
FaceOfPast* past = j;
Этот раздел кода переходит к базовому классу. Поэтому, когда вы делаете следующее
future->Smile();
past->Smile();
Это фактический указатель на FaceofPast и FaceOfFuture.