Последнее обновление:2026-10-12
C++ виртуальные функции virtual¶
Об авторе:¶
Личный официальный аккаунт:

1.Виртуальные функции и полиморфизм времени выполнения¶
Соответствующий код: emp.cpp
Вызов виртуальной функции определяется типом объекта, на который указывает указатель или на который ссылается ссылка, а не типом самого указателя или ссылки.
2.vptr и vtable¶
См. vptr_vtable那些事
3.Аргументы по умолчанию в виртуальных функциях¶
Соответствующий код: default_arg.cpp
Аргументы по умолчанию связываются статически, а виртуальные функции — динамически. Использование аргумента по умолчанию определяется типом самого указателя или ссылки, а не типом объекта.
4.Можно или нельзя¶
(1) Можно ли объявить статическую функцию виртуальной?
Причин в основном две:
Статическую функцию нельзя объявить виртуальной; кроме того, её нельзя модифицировать ключевыми словами const и volatile
Функция-член static не принадлежит ни одному объекту или экземпляру класса, поэтому добавление к этой функции virtual не имеет никакого смысла.
Виртуальные функции работают через vptr и vtable. vptr — это указатель, который создаётся в конструкторе класса и доступ к которому возможен только через указатель this; у статической членской функции нет указателя this, поэтому она не может получить доступ к vptr.
Код для изучения: static_error.cpp
(2)Может ли конструктор быть виртуальным?
Конструктор нельзя объявить виртуальным. Кроме того, помимо inline и explicit, конструктору не разрешается использовать никакие другие ключевые слова.
Почему конструктор не может быть виртуальным?
Хотя таблица виртуальных функций vtable строится уже на этапе компиляции, указатель на таблицу виртуальных функций vptr возникает лишь на этапе выполнения, при инстанцировании объекта. Если класс содержит виртуальные функции, компилятор добавляет в конструктор код создания vptr. И тут возникает проблема: если конструктор виртуальный, ему нужен vptr для доступа к vtable, но в этот момент vptr ещё не создан. Поэтому конструктор не может быть виртуальной функцией.
Мы используем виртуальные функции потому, что нуждаемся в полиморфном выполнении при неполной информации. Конструктор же служит для инициализации экземпляра, а тип экземпляра должен быть определён однозначно. Поэтому объявлять конструктор виртуальным нет необходимости.
Код для изучения:
(3)Может ли деструктор быть виртуальным?
Деструктор можно объявить виртуальным. Если нам нужно удалить базовый указатель, указывающий на производный класс, деструктор следует объявить виртуальным. Фактически, если класс может быть унаследован другими классами, следует объявлять виртуальный деструктор (даже если этот деструктор ничего не делает).
Код для изучения:
(4)Может ли виртуальная функция быть приватной?
- Когда указатель базового класса указывает на объект класса-наследника, вызывается функция объекта класса-наследника;
- int main() должна быть объявлена дружественной классу Base, иначе компиляция завершится ошибкой. Компилятор сообщит: ptr не может получить доступ к приватной функции. Разумеется, если объявить базовый класс как public, а класс-наследник как private, этой проблемы не возникнет.
Код для изучения:
(5)Может ли виртуальная функция быть встроенной (inline)?
Обычно компилятор рассматривает членские функции класса как кандидатов на встраивание. Но виртуальная функция, вызываемая через указатель или ссылку базового класса, обязательно не может быть встроена. Конечно, вызов виртуальной функции объектом-значением или статический вызов могут быть встроены; статический вызов виртуального деструктора также обязательно разворачивается встраиванием.
- Виртуальная функция может быть inline-функцией; встраивание может модифицировать виртуальную функцию, однако когда виртуальная функция проявляет полиморфизм, встраивание невозможно.
- Inline — это предложение компилятору выполнить встраивание на этапе компиляции, а полиморфизм виртуальной функции проявляется во время выполнения; компилятор не может знать, какой код будет вызван в рантайме, поэтому когда виртуальная функция проявляет полиморфизм (во время выполнения), встраивание невозможно.
inline virtualможет быть встроена лишь в одном случае: когда компилятор знает, объект какого класса вызывается (например,Base::who()); это происходит, только когда у компилятора есть сам фактический объект, а не указатель или ссылка на объект.
Код для изучения:
5.RTTI и dynamic_cast¶
RTTI (Run-Time Type Identification) — механизм, благодаря которому программа может через указатель или ссылку базового класса проверять фактический производный тип объекта, на который они указывают или ссылаются.
В объектно-ориентированном программировании иногда требуется во время выполнения выяснить, можно ли использовать объект как некоторый полиморфный тип. Аналогично instanceof в Java и операторам as и is в C#, C++ предоставляет функцию dynamic_cast для динамического приведения типов. По сравнению с C-стилем приведения и reinterpret_cast в C++, dynamic_cast выполняет проверку безопасности типов — это преобразование на основе запроса возможностей (Capability Query), поэтому для преобразований между полиморфными типами рекомендуется применять именно dynamic_cast.
Код для изучения:
6.Чисто виртуальные функции и абстрактные классы¶
См. Чистые виртуальные функции и абстрактные классы
Похожие темы¶
- C++ чисто виртуальные функции и абстрактные классы | C++那些事
- C++ утверждения assert | C++那些事
- C++ битовые поля | C++那些事
- Реализация полиморфизма C++ на языке C | C++那些事
- C++ константы const и его модификация | C++那些事
- C++ всё семейство const | C++那些事
- C++ оператор разыменования * | C++那些事
- C++ инициализация и присваивание | C++那些事
- C++ возврат нескольких значений | C++那些事
- C++ ключевое слово mutable | C++那些事