Skip to content

Last updated:2026-10-13

C++ Virtual Functions

About the Author:

Personal WeChat official account:

C++ Virtual Functions | C++那些事

1. Virtual Functions and Runtime Polymorphism

Corresponding code: emp.cpp

The invocation of a virtual function depends on the type of the object pointed to or referred to, not on the type of the pointer or reference itself.

2. vptr and vtable

See vptr and vtable

3. Default Arguments in Virtual Functions

Corresponding code: default_arg.cpp

Default arguments are statically bound, while virtual functions are dynamically bound. Which default argument is used depends on the type of the pointer or reference itself, not on the type of the object.

4. Is It Allowed?

(1) Can a static function be declared virtual?

There are two main reasons:

A static function cannot be declared virtual, nor can it be qualified with the const or volatile keywords

A static member function does not belong to any class object or class instance, so adding virtual to such a function is meaningless.

Virtual functions rely on the vptr and the vtable. The vptr is a pointer, created in the class's constructor, and can only be accessed through the this pointer; static member functions have no this pointer, so they cannot access the vptr.

Code to study: static_error.cpp

(2) Can a constructor be virtual?

A constructor cannot be declared virtual. Moreover, apart from inline and explicit, a constructor may not use any other keyword.

Why can't a constructor be virtual?

Although the virtual function table (vtable) is built at compile time, the pointer to the virtual function table (vptr) is only generated at runtime when the object is instantiated. If a class contains virtual functions, the compiler adds code in the constructor to create the vptr. Here comes the problem: if the constructor were virtual, it would need the vptr to access the vtable, but at that moment the vptr does not exist yet. Therefore, a constructor cannot be virtual.

The reason we use virtual functions is that we need polymorphic dispatch with incomplete information. A constructor, on the other hand, is used to initialize an instance, and the type of the instance must be definite. Therefore, there is no need to declare a constructor virtual.

Code to study:

(3) Can a destructor be virtual?

A destructor can be declared virtual. If we need to delete a base-class pointer that points to a derived class, the destructor should be declared virtual. In fact, as long as a class might be inherited by other classes, a virtual destructor should be declared (even if that destructor does nothing).

Code to study:

(4) Can a virtual function be private?

  • When a base-class pointer points to a derived-class object, the derived class's function is invoked;
  • int main() must be declared a friend of the Base class, otherwise compilation fails. The compiler reports an error: ptr cannot access the private function. Of course, if the base class is declared public and the derived class private, the problem disappears.

Code to study:

(5) Can a virtual function be inlined?

Usually the compiler considers whether to inline class member functions. But a virtual function invoked through a base-class pointer or reference can never be inlined. Of course, when a virtual function is called on an actual object or invoked statically, it can be inlined, and a statically invoked virtual destructor will certainly be expanded inline.

  • A virtual function can be an inline function; inline is allowed to qualify a virtual function, but a virtual function cannot be inlined when it exhibits polymorphic behavior.
  • Inlining is a suggestion to the compiler to inline at compile time, while the polymorphism of virtual functions happens at runtime; the compiler cannot know which code will be invoked at runtime, so a virtual function cannot be inlined when it behaves polymorphically (at runtime).
  • The only time inline virtual can be inlined is when the compiler knows which class the object being called belongs to (for example Base::who()), which only happens when the compiler has an actual object rather than a pointer or reference to the object.

Code to study:

5. RTTI and dynamic_cast

RTTI (Run-Time Type Identification) allows a program to use a base class's pointer or reference to check the actual derived class type of the object they point to.

In object-oriented programming, we sometimes need to query at runtime whether an object can be used as a certain polymorphic type. Similar to Java's instanceof and C#'s as and is operators, C++ provides dynamic_cast for dynamic casting. Compared with C-style casts and C++ reinterpret_cast, dynamic_cast provides type-safety checks and is a capability-query-based conversion, so dynamic_cast is the preferred choice for conversions between polymorphic types.

Code to study:

6. Pure Virtual Functions and Abstract Classes

See Pure Virtual Functions and Abstract Classes

Comments