C++ polymorphism and virtual functions

Directory structure

1. Concept

1.1 Polymorphism

1.2 Virtual functions

2. Virtual function

2.1 Function

2.2 Can the destructor be a virtual function?

2.3 Can the constructor be a virtual function?

2.4 Pure virtual functions

2.5 Principle of virtual table? Where are the virtual table pointers stored?

2.6 Does a virtual function appear in the definition of a class or when it is an object?

2.4 Differences between function overloading and rewriting

2.5 C++ Polymorphism Example

to readers

1. Concept

1.1 Polymorphism

Objects of different classes react differently to the same method or function. The implementation of polymorphism relies on virtual functions

Static polymorphism and dynamic polymorphism

Static polymorphism (compile-time polymorphism)

  1. This is a form of polymorphism achieved through methodoverloading.
  2. At compile time, the compiler determines which method to use. The selection of overloaded methods occurs during the compilation phase, so it is static and the compiler determines which method to call.

Dynamic polymorphism (runtime polymorphism)

  1. This is a form of polymorphism achieved through methodoverriding and inheritance.
  2. At run time, the program determines which method to use.

1.2 Virtual function

A virtual function refers to a function that has been modified with the modifier virtua, and the function that defines the virtual function must be a member function of the class. After the virtual function is inherited, the derived class inherited is a virtual function. Destructors can be defined as virtual functions, but constructors (and friend functions) cannot be defined as virtual functions.

2. Virtual function

2.1 Function

The main role of virtual functions is to implement the polymorphism mechanism. The base class defines a virtual function, and subclasses can override the function; when rewriting the accumulated defined virtual function in a derived class, the method needs to be declared as a virtual method in the derived class.

2.2 Can the destructor be a virtual function

When using the polymorphic feature to let the base class pointer point to the derived class object, if the destructor is not a virtual function, when the derived class object is destroyed through the base class pointer, the statically bound destructor will be called, which is the destructor of the base class. The constructor can only destroy elements belonging to the base class, resulting in incomplete destruction of the derived class, and the program will suffer resource leaks or undefined behavior.

2.3 Can the constructor be a virtual function

In C++, constructors (including copy constructors and move constructors) cannot be declared virtual. Virtual functions are called through the object’s virtual function table (vtable) at runtime, and the constructor is executed before the object is created. Therefore, the virtual function table does not exist before the object exists, and polymorphism of virtual functions cannot be achieved.

2.4 Pure virtual function

Pure virtual functions must be defined in the base class and have no specific implementation code, only function declarations. It stipulates that derived classes must provide a concrete implementation of the function.

Note: Classes with pure virtual functions cannot be instantiated and can only be used as base classes to derive other classes. Derived classes must provide specific implementations of corresponding pure virtual functions.

Example (The syntax for defining a pure virtual function is to add = 0 after the function declaration):

class AbstractBase {
public:
    virtual void pureVirtualFunction1() = 0; // The first pure virtual function
    virtual void pureVirtualFunction2(int x) = 0; // The second pure virtual function takes parameters
    virtual void pureVirtualFunction3(double y, const std::string & amp; str) = 0; // The third pure virtual function takes multiple parameters
};
};

class Derived : public AbstractBase {
public:
    void pureVirtualFunction1() override { //override indicates that the virtual function is rewritten and can be omitted
        // Provide specific implementation
        // ...
    }

    void pureVirtualFunction2(int x) override {
        // Provide specific implementation
        // ...
    }

    void pureVirtualFunction3(double y, const std::string & amp; str) override {
        // Provide specific implementation
        // ...
    }
};

2.5 Principle of virtual table? Where are the virtual table pointers stored?

  1. How virtual tables work:

    • Every C++ class that contains virtual functions has a corresponding virtual function table.
    • The virtual table stores the addresses of virtual functions in this class.
    • Each object contains a vtable pointer to its class.
    • When you call a virtual function, you actually look up the appropriate virtual function address through the object’s vtable pointer, and then call the function.
  2. Where is the virtual table pointer stored: It is usually stored inside the object, that is, the address of the object is the address of the virtual table pointer.

2.6 Does a virtual function appear when the definition of a class or an object appear

Virtual functions are defined in the class definition, not when the object is created.

2.4 The difference between function overloading and rewriting

  1. Scope difference: overriding and overridden functions are in different classes, and overloading and overloaded functions are in the same class.
  2. Parameter differences: The overridden function name, parameter number, type, order and return value type are exactly the same, while the overloaded parameter number, type, and order have at least one difference.
  3. The difference between virtual: The overridden base class function must be modified by virtual. The overloaded function and the overloaded function can be modified by virtual or not.

2.5 C++ Polymorphism Example

#include <iostream>
using namespace std;

classShape {
public:
    virtual double area() const = 0; // pure virtual function
};

class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    double area() const {
        return 3.14159265359 * radius * radius;
    }
};

class Rectangle : public Shape {
private:
    double width;
    double height;

public:
    Rectangle(double w, double h) : width(w), height(h) {}

    double area() const {
        return width * height;
    }
};

int main() {
    Circle circle(5.0);
    Rectangle rectangle(4.0, 6.0);

    Shape* shape1 = & amp;circle;
    Shape* shape2 = & amp;rectangle;

    std::cout << "Area 1: " << shape1->area() << std::endl;
    std::cout << "Area 2: " << shape2->area() << std::endl;

    return 0;
}

To the readers

It is not difficult to know, but difficult to do; it is difficult not to do, but ultimately difficult