Что происходит при вызове базовых функций из производных классов?

Ну, мой вопрос немного сложнее.
Допустим, у меня есть три класса примерно так:

    class GrandFather
{
public:
virtual int DoSomething()
{
return 3;
}
};

class Father : public GrandFather
{
};

class Child : public Father
{
public:
virtual int DoSomething()
{
Father::DoSomething();
}
};

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

Теперь я хочу знать, как это можно сделать.

Класс Child имеет указатель на свою собственную vtable, которая будет указывать, что при вызове DoSomething() Реализация ребенка называется.

Класс Father также имеет указатель на свой собственный vtable, который будет указывать, что при вызове DoSomething() реализация дедушкина называется.

Когда я использую Father::DoSomething() внутри ребенка, это должно вызвать GrandFather::DoSomething(),
но как ребенок может сказать, где находится функция?

Если название искажения действительно используется, то как? потому что нет функции с этим именем (что-то вроде _Father_DoSomething(this)).

Ребенок должен получить доступ к vptr Отца, чтобы добраться до GrandFather::DoSomething() Что, насколько я знаю, он не может.

Это беспокоит меня уже довольно давно, поэтому я буду очень признателен за помощь.
Спасибо 🙂

-2

Решение

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

То, что зовет Father::DoSomething вызывает GrandFather :: DoSomething из-за наследования, которое является отдельным механизмом. Если вы ссылаетесь на член производного класса, а его там нет, он поднимается на один уровень вверх и ищет его в базовом классе. Там нет необходимости виртуального вызова.

1

Другие решения

Когда вы звоните Father::DoSomething от Child компилятор использовал информацию о времени компиляции, чтобы решить, что вызывать. Потому что нет версии в Father он знает, чтобы позвонить в GrandFather так как это версия, которая эффективно в рамках Father,

Когда переопределенный метод вызывает базовую реализацию, vtable не используется. Vtable используется только при вызове метода через указатель или ссылку. Вот как весь полиморфизм достигается в C ++.

Название искажения не имеет ничего общего с этим. По сути, искажение имени — это то, как кодируется имя метода, поэтому дайте уникальное имя для метода / функции. Никакие два метода / функции не будут иметь одно искаженное имя.

1

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

1

Команда g ++ file_name.cpp -S может предоставить достаточно информации, чтобы прояснить все проблемы, связанные с vtable. Пожалуйста, проверьте .s файл производится:

У child есть собственная vtable, которая содержит vtable отца, который, в свою очередь, содержит vtable для GrandFather:

vtable for Child:
Child::DoSomething()
vtable for Father

vtable for Father:
GrandFather::DoSomething()
vtable for GrandFather

vtable for GrandFather:
GrandFather::DoSomething()

Когда я использую Father :: DoSomething () внутри Child, он должен вызывать
GrandFather :: DoSomething (), но как ребенок может сказать, где функция
является?

В vtable for Father есть ссылка на GrandFather :: DoSomething (), поэтому, вызывая Father :: DoSomething (), вы на самом деле вызываете GrandFather :: DoSomething ()

1
  1. «Название искажения» не имеет к этому никакого отношения. Техника, известная как «искажение имени», относится к совершенно другой и не относящейся к делу области. Я не знаю, откуда у вас возникла идея задействовать это здесь.

  2. Классы не имеют никаких «указателей на vtable». Только конкретные объекты (также известные как экземпляры) типов классов могут иметь такие указатели. Не смешивай классы а также объекты класса. В любом случае «указатели vtable» — это детали реализации, которых нет на уровне языка. Это совершенно не нужно для понимания поведения кода на уровне языка.

  3. В вашем примере у вас нет никаких объектов вообще. Вы просто объявили кучу классов. По этой причине рано принимать какие-либо «указатели на vtables». У вас нет никаких указателей на какие-либо таблицы в том, что вы опубликовали.

  4. Когда дело доходит до вопроса Father::DoSomething() Вызов, вопрос каких-либо «виртуальных таблиц» вообще не входит в картину, даже как деталь реализации. Father::DoSomething() вызов использует квалифицированный имя целевой функции. Такие квалифицированные вызовы всегда разрешаются напрямую, без участия виртуальных таблиц. То есть при выполнении Father::DoSomething() вы явно просите компилятор игнорировать любой полиморфизм (игнорировать любые «vtables») и напрямую выполнять поиск имени во время компиляции для name DoSomething в классе Father, По правилам поиска имени он найдет функцию GrandFather::DoSomething() и позвони.

  5. Если вы на самом деле объявляете объект, как

    Child child;
    

    тогда у вас будет GrandFather подобъект встроен в Father подобъект, встроенный в вышеупомянутое Child объект. Все «указатели vtable» в этих вложенных объектах будут указывать на vtable Child (на самом деле, в типичной реализации все эти вложенные объекты будут иметь один указатель vtable).

    Теперь, если вы позвоните

    child.DoSomething()
    

    этот вызов будет решен в соответствии с таблицей Child и отправили в Child::DoSomething() (большинство компиляторов на самом деле достаточно умны, чтобы оптимизировать код и отправлять вызов напрямую). Однако, как я уже сказал выше, квалифицированный Father::DoSomething изнутри Child::DoSomething безоговорочно выполняется напрямую, как вы просили. Это не заботится ни о каких vtable указателях. Это просто идет прямо к GrandFather::DoSomething(),

Это все, что нужно сделать.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector