Пример для начала.
class A
{
public:
virtual const char* GetName() { return "A"; }
};
class B: public A
{
public:
virtual const char* GetName() { return "B"; }
};
class C: public B
{
public:
virtual const char* GetName() { return "C"; }
};
class D: public C
{
public:
virtual const char* GetName() { return "D"; }
};
int main()
{
C cClass;
A &rBase = cClass;
cout << "rBase is a " << rBase.GetName() << endl;
return 0;
}
В этом конкретном примере выходные данные:
rBase - это C
Вот шаги, как это работает:
rBase - указатель типа A, поэтому он переходит в класс A и ищет GetName ().Но GetName () там виртуален, поэтому компилятор проверяет все классы между A и C и берет функцию GetNAme () из самого производного класса, т.е. C
Но я сомневаюсь, что как компилятор узнает, какиеявляется потомком класса A и как он сможет перейти в класс B и т. д. из родительского класса в дочерний класс?Ребенок знает, что это родитель, но родитель не знает, что это дети (я думаю!).
С моей точки зрения, правильные шаги выполнения должны были быть:
rBaseявляется указателем типа A, поэтому он переходит к классу A и ищет GetName ().Но GetName () является виртуальным, поэтому компилятор проверяет, на какой класс указывает указатель.Объект класса C в этом случае переходит к классу C и проверяет, есть ли у него функция GetName (), поэтому использует его.Если функция отсутствует в классе C (предположим), компилятор может легко отследить родительский элемент C и проверить его, и это может продолжаться до тех пор, пока не достигнет A (при условии, что все классы, кроме A, не содержат GetName ()).
Теперь этот метод кажется более логичным, поскольку перемещение назад (от потомка к родителю) в дереве наследования кажется более достижимым, чем перемещение вперед (от родителя к потомку).
С уважением,