Виртуальные функции - где указатель? - PullRequest
1 голос
/ 10 апреля 2011

Пример для начала.

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 ()).

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

С уважением,

Ответы [ 2 ]

4 голосов
/ 10 апреля 2011

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

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

2 голосов
/ 10 апреля 2011

rBase - указатель типа A, поэтому он переходит в класс A и ищет GetName ().Но GetName () там виртуален, поэтому компилятор проверяет все классы между A и C и берет функцию GetNAme () из самого производного класса, т.е. C

Нет, это не так, как это работает.Компилятор при генерации кода для вызова virtual GetName() через указатель на A просто помещает что-то вроде «вызов 7-й записи в виртуальной таблице, связанной с объектом».При создании производного класса, который переопределяет GetName(), компилятор поместит свою реализацию GetName() в эту запись в виртуальной таблице.

Таким образом, родительскому классу не нужно знать своих потомков.Дети несут ответственность за правильное заполнение виртуальной таблицы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...