T.TAO
Back to Blog
/4 min read/Blog

Tags:

C++ Programming Content List C++ Programming #5 Functions

  • ComputerScience

#GameEngine#ComputerScience#SWE

This note mainly covers the polymorphism feature of C++ OOP. Polymorphism is one of the core features of OOP, referring to the same interface with different implementations. In C++, polymorphism allows us to call derived class functions through base class pointers or references.

Polymorphism typically has two forms:

  • Static polymorphism, also called compile-time polymorphism, is implemented through function overloading and templates.
  • Dynamic polymorphism, also called runtime polymorphism, is implemented through virtual functions and inheritance.

Simply put, polymorphism is the ability for a message to be presented in multiple forms.

Dynamic Polymorphism

Dynamic polymorphism is implemented through virtual functions and inheritance. In the notes on functions, we have already introduced the concept of virtual functions. Here we review it in detail and gain a deeper understanding of virtual functions and the role of the virtual table.

Virtual Functions

A virtual function is a member function that allows the program to determine at runtime which function to call based on the actual type of the object. It is marked with the virtual keyword in the base class and overridden with the override keyword in the derived class to achieve dynamic binding. For example,

In the Base class, we define a virtual function func(). In the derived class Derived, we can override this function with the override keyword.

At this point, if we output the result in main(),

Note that although obj is a Base class pointer, the way it was created determines that it is actually a Derived object. Therefore, when calling func(), the overridden func() from the Derived class will be used.

The key point illustrated in the above example:

Virtual functions implement dynamic binding—that is, calling the appropriate function at runtime based on the actual type of the object, rather than deciding at compile time. If decided at compile time, it is called static binding. A non-virtual function not marked with the virtual keyword uses static binding.

Virtual Table

The virtual table (VTable, virtual function table) is a data structure generated for each class that contains virtual functions. The virtual table stores the addresses of the virtual functions in the class. Each object has a hidden pointer (called vptr) that points to the virtual table of its class. When a virtual function is called, the program looks up the function address in the virtual table through vptr and executes it.

We use the example above to understand the implementation mechanism of the virtual table.

Here, we:

  1. First create an object of the Derived class named obj. obj is a pointer to Base.
  2. The compiler checks whether obj calls the virtual function func().
  3. The program uses vptr to point to the Derived class's virtual table.
  4. Find the address of Derived::func() in the Derived class's virtual table and call the derived class's implementation.

Virtual Destructor

Pay special attention: if a base class needs to release derived class objects through a pointer, the base class destructor must be declared as virtual; otherwise, it will cause resource leaks. For example, in the following case,

Note that the Derived class inheriting from Base has a private array data. In the Derived class constructor we allocate space for data, and in the destructor we release it. Therefore, in the main function, obj as a Base class pointer must be able to correctly call the Derived class destructor when it is deleted. Thus, the destructor in the Base class must be virtual.

To emphasize again: it must be virtual.

Additionally, destructors behave differently from ordinary virtual functions: when a virtual destructor is called, the derived class destructor is called first, then the base class destructor, rather than only calling the derived class function. This ensures that resources are correctly released when destroying the object.

Therefore, the output of main() above would be,

Constructors

In C++, constructors cannot be virtual functions. We have mentioned in previous notes:

  • Construction order: the base class constructor is called first, then the derived class constructor.
  • This is because the base class part must be constructed first before the derived class can extend upon it.

When calling a virtual function inside a constructor, the overridden version from the derived class will not be called; instead, the version defined in the current class will be called, maintaining compile-time binding. The reason for this behavior is that when the base class constructor executes, the derived class object is not yet fully constructed, and the derived class portion's virtual table has not been established—the virtual table pointer is only formed after the constructor call completes.

Static Polymorphism