In depth analysis of virtual functions and polymorphism in C++

  • 2020-04-02 01:38:40
  • OfStack

1. Virtual functions in C++
The role of virtual functions in C++ is mainly to implement the mechanism of polymorphism. For polymorphism, it is simply a matter of pointing to an instance of a subclass with a pointer to the parent, and then calling a member function of the actual subclass with a pointer to the parent. This technique allows Pointers to parent classes to have "multiple morphologies," which is a generic technique. A generic technique is simply an attempt to implement a variable algorithm using immutable code. For example: templating techniques, RTTI techniques, virtual function techniques, either attempt to achieve compile-time resolution, or attempt to achieve run-time resolution.

Anyone familiar with C++ should know that Virtual functions are implemented through a Virtual Table and a pointer to the Virtual Function Table (VPTR). Virtual function table (VTBL for short) plays an important role in realizing polymorphism. In this table, the addresses of virtual functions in a class are mainly stored. This table solves the problem of inheritance and overwriting, ensuring that its contents can truly reflect the actual functions. Each instance of a class that contains virtual functions contains a CPTR pointer to the first address of the virtual function table. We can use this pointer to find the virtual function to access, complete the virtual function call mainly includes: find the first address of the virtual function table (VPTR), through CPTR to find the address of the virtual function to use, call the virtual function. Then using virtual functions you always want to consider the problem of efficiency, in fact, in order to improve the efficiency of c + + compiler is to ensure that the virtual function table pointer to the object instance in the foremost position, this is to ensure that pick up to the virtual function table has the highest performance, which means that we this virtual function is obtained by the address of an object instance tables, can be found by traversing the table then the virtual function address, and then call the corresponding function. Take a look at the following code:


#include <iostream>
using namespace std;
class Base 
{
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
};
typedef void(*Fun)(void);

int main()
{
    Base b;
    Fun pFun = NULL;
    cout << " Virtual function table address: " << (int*)(&b) << endl;
    cout << " Virtual function table   -   The first function address: " << (int*)*(int*)(&b) << endl;
    pFun = (Fun)*((int*)*(int*)(&b));
    pFun();
    return 0;
}

In this example, you can see that you get the address of the virtual function table (VPTR) by forcing the &b to int*, and then, by addressing it again, you get the address of the first virtual function, which is Base::f(), which was verified in the above program (forcing the int* to the function pointer). From this example, we can know that if we want to call Base::g() and Base::h(), the code is as follows:

(Fun)*((int*)*(int*)(&b)+0); // Base::f()

(Fun)*((int*)*(int*)(&b)+1); // Base::g()
(Fun)*((int*)*(int*)(&b)+2); // Base::h()

You can see how the graph of the virtual function table is drawn:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201309/201309240954264.jpg ">

As you all know, polymorphism is achieved by inheritance, so let's talk about virtual function inheritance. Inheritance involves the overwriting of virtual functions, and what do virtual functions that are not actually overwritten have to do with polymorphism? Here we discuss what the covered virtual function table looks like, assuming the following inheritance relationship:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201309/201309240954265.jpg ">

See what a virtual function looks like:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201309/201309240954266.jpg ">

You can see that Base: : f () is overwritten, so if you assign an instance of Derive to a Base class, Base pointer pBase, by pBase- > (f); Then you're accessing f () in the subclass, which completes the polymorphism. So what does the virtual function table look like? Take a look at the following four sentences to see!

1. Virtual functions are placed in the table in the order in which they are declared.

2. The virtual function of the parent class precedes the virtual function of the subclass.

3. The overridden f() function is placed in the position of the original superclass virtual function in the virtual table.

4. Functions that are not overwritten remain the same.

2. Implement polymorphism with virtual functions
Take a look at the polymorphic code below:


#include <iostream>
using namespace std;
class Base
{
public:
    virtual void Print()
    {
        cout<<"Base::Print()"<<endl;
    }
};
class Derive : public Base
{
public:
    virtual void Print()
    {
        cout<<"Derive::Print()"<<endl;
    }
};
int main()
{
    Derive derive;
    Base *pBase = &derive;
    pBase->Print();
    return 0;
}
//Polymorphic code

The implementation of virtual function code, must remember: must be the base class pointer to the subclass of the object address. First of all, try to understand the principle of using virtual function to achieve polymorphism, if you really do not understand why virtual function can achieve polymorphism, and why so to achieve polymorphism, search the Internet again search the relevant information!!


Related articles: