#c #overriding #virtual #vtable
#c #переопределение #виртуальный #vtable
Вопрос:
Итак, если у меня есть
class base
{
public:
virtual void start();
virtual void stop();
void doSomething() { start(); .... stop(); }
}
class derived : public base
{
public:
void start();
void stop();
}
вызов derived.doSomething()
вызовет derived::start() и derived::stop() .
НО это работает только в том случае, если они виртуальные.
Я хотел знать, почему это не работает без virtual
ключевого слова, что означает детали более низкого уровня. Я не могу много найти об этом в Интернете…
Спасибо!
Комментарии:
1. Я могу порекомендовать cppreference.com статья о виртуальных функциях.
2. Это не работает без
virtual
becausevirtual
, как вы сообщаете базовому классу, что функция может быть переопределена в производном классе, и, таким образом, выяснить во время выполнения, какую версию вызывать. Таким образом, вопрос действительно является тавтологией. Это не работает без того, что заставляет его работать! Я уверен, что в Интернете должно быть много ресурсов об этом.
Ответ №1:
Без ключевого слова virtual код base::doSomething НЕ имеет ПРЕДСТАВЛЕНИЯ о производных версиях этих методов (подумайте о разрешении компоновщика)
Комментарии:
1. Это в значительной степени размахивает руками. Даже при
virtual
doSomething
этом код не имеет представления о производных версиях этих функций.virtual
сообщает компилятору, что производные классы могут переопределять эти функции, и в ответ компилятор генерирует код для поддержки переопределения. Но если производный класс не переопределяет эти функции, код остается действительным. (virtual void start() = 0;
было бы по-другому, но это не упоминается в вопросе.2. @PeteBecker Ключевое слово virtual сообщает компоновщику разрешить вызов через виртуальную таблицу, которая должна быть разрешена во время выполнения. Не будучи виртуальной, компоновщик преобразует ее в статический вызов. Это то, что я имел в виду, говоря «БЕЗ ПОНЯТИЯ» (статически связанный или виртуальный вызов таблицы, который подразумевает НЕКОТОРОЕ ПРЕДСТАВЛЕНИЕ о возможной производной реализации).
3. Опять же: код, сгенерированный для базового класса, ничего не знает ни о каких производных классах. Он предоставляет механизм, который производные классы могут использовать для переопределения виртуальных функций базового класса.
Ответ №2:
Если функция объявлена виртуальной, все переопределения этой функции хранятся в таблице виртуальных функций-членов (vtable). Когда такая функция вызывается в вашем коде, программа на самом деле просто указывает на расположение таблицы. Затем таблица используется для определения правильной функции для вызова во время выполнения.
Если функция не объявлена виртуальной, start()
и stop()
в реализации базового класса doSomething
всегда просто указывают на определение базового класса этих функций.
C позволяет выбирать между обоими механизмами, поскольку поиск во время выполнения в vtable приводит к некоторым накладным расходам, которые C обычно не навязывает пользователю — «вы платите только за то, что используете».
Разделы примеров и вызова в вики-статье довольно хорошо показывают техническую сторону.