C++ virtual function and virtual table in-depth analysis

1. How are virtual functions found

First look at single inheritance, compile the following code:

#include<iostream>
#include<string.h>
using namespace std;

class Grandam
{
public:
    virtual void introduce_self()
    {
        cout << "I am grandam." << endl;
    }
};

class Mother:public Grandam
{
public:
    virtual void introduce_self()
    {
        cout << "I am mother." << endl;
    }
};

class Daughter :public Mother
{
public:
    void introduce_self()
    {
        cout << "I am daughter." << endl;
    }
};

int main()
{
    Grandam* ptr;
    Grandam g;
    Mother m;
    Mother m1;
    Daughter d;

    typedef void(*introduce_self)(); 

    ptr = &g;
    ptr->introduce_self();

    ptr = &m;
    ptr->introduce_self();

        ptr = &m1;

    ptr = &d;
    ptr->introduce_self();

    introduce_self* p = (introduce_self*)(*(int*)(&g));  
    (*p)();

    system("pause");
    return 0;
}

The result is shown in the figure below:

Write picture description here

The fourth result is that the virtual function in the base class Grandam is called.

In the code:

typedef void(*introduce_self)();   //对函数进行重命名

introduce_self* p = (introduce_self*)(*(int*)(&g));  //定义一个函数指针,并让它指向对象内存的首地址里存放的地址(即虚表指针或地址)

(*p)();   //对虚表调用该函数

因为虚表里存放的是虚函数的地址,所以对函数指针进行解引用,即就是调用该虚函数。

Thus we know:

只要存在虚函数,那么被基类指针或被基类对象引用的对象中调用该虚函数时,该被调用对象的首地址存放的是虚表指针_vfptr(地址),而虚表_vfptr中存放的是虚函数地址。

2. Whether the base class and derived class share a virtual table

We can also single-step debug it by pressing f10 to see the changes in the contents stored in the memory, as shown in the figure:

1. The memory when calling the function introduce_self() of the base class Grandam object g is shown in the following figure:

Write picture description here

2. The memory when calling the function introduce_self() of the derived class Mother object m is shown in the figure below:

Write picture description here

3. The memory when calling the function introduce_self() of the derived class Mother object m is shown in the figure below:

Write picture description here

4. The memory when the function introduce_self() of the derived class Daughter object d is called is shown in the following figure:

Write picture description here

Summary: According to the comparison of the three memories, we know that the virtual table pointers stored in different object addresses are different, and objects of the same type share a virtual table.

3. The order in which the base class and the derived class call the virtual function

1. Single inheritance, the code is as follows:

#include<iostream>
#include<string.h>
using namespace std;

class Grandam
{
public:
    void introduce_self1()
    {
        cout << "1: I am grandam." << endl;
    }

    void introduce_self2()
    {
        cout << "2: I am grandam." << endl;
    }

    virtual void introduce_self3()
    {
        cout << "3: I am grandam." << endl;
    }

    virtual void introduce_self4()
    {
        cout << "4: I am grandam." << endl;
    }
};


class Daughter :public Grandam
{
public:

    void introduce_self2()
    {
        cout << "2: I am daughter." << endl;
    }

    virtual void introduce_self5()
    {
        cout << "5: I am daughter." << endl;
    }

    virtual void introduce_self1()
    {
        cout << "1: I am daughter." << endl;
    }

    virtual void introduce_self4()
    {
        cout << "4: I am daughter." << endl;
    }

};

int main()
{
    Grandam g;
    Daughter d;
    Daughter* ptr = &d;

    typedef void(*intro)();
    intro* pi = (intro*)(*((int*)(&d)));
    while (*pi)
    {
        (*pi)();
        (int *)pi++;
    }
    return 0;
}

**The following code is a call to the virtual function in the virtual table. The precautions are as follows:

typedef void(*intro)();
    intro* pi = (intro*)(*((int*)(&d)));
    while (*pi)
    {
        (*pi)();
        (int *)pi++;
    }

Note : It is a call to the virtual table of the derived class object d. This is running under VS 2012. Because the last address of the virtual table is 00000000, it can be written as this while loop. Different compilers have different writing methods.

The results of the operation are as follows:

Write picture description here

In summary, we can see that under single inheritance :
according to the virtual table pointer stored in the object address: the order of the address of the virtual function stored in the virtual table, to call the virtual function:

1). First call the virtual functions in the base class, and call them in the order declared in the base class.

2). After the virtual function of the base class is called, call the virtual function of the derived class and put it behind the base class function.

3). When the virtual function of the derived class is called, it is also called in the order of declaration in the derived class.

2. Multi-inheritance is as follows:

#include<iostream>
#include<string.h>
using namespace std;

class Grandam
{
public:
    void introduce_self1()
    {
        cout << "1: I am grandam." << endl;
    }

    void introduce_self2()
    {
        cout << "2: I am grandam." << endl;
    }

    virtual void introduce_self3()
    {
        cout << "3: I am grandam." << endl;
    }

    virtual void introduce_self4()
    {
        cout << "4: I am grandam." << endl;
    }
};

class Mother 
{
public:
    virtual void introduce_self1()
    {
        cout << "1: I am mother." << endl;
    }

    virtual void introduce_self2()
    {
        cout << "2: I am mother." << endl;
    }
};

class Daughter :public Grandam , public Mother
{
public:

    void introduce_self2()
    {
        cout << "2: I am daughter." << endl;
    }

    virtual void introduce_self5()
    {
        cout << "5: I am daughter." << endl;
    }

    void introduce_self1()
    {
        cout << "1: I am daughter." << endl;
    }

    virtual void introduce_self4()
    {
        cout << "4: I am daughter." << endl;
    }

};

int main()
{
    Grandam g;
    Mother m;
    Daughter d;
    Mother& m1 = d;
    Grandam& g1 = d;
    typedef void(*pfun)();
    pfun* pg1 = (pfun*)(*(int*)(&g1));
    while (*pg1)
    {
        (*pg1)();
        (int*)pg1++;
    }

    pfun* pm1 = (pfun*)(*(int*)(&m1));
    while (*pm1)
    {
        (*pm1)();
        (int*)pm1++;
    }
    return 0;
}

The result is as follows:

Write picture description here

Summary : In the case of multiple inheritance , the virtual function of the derived class is called after the virtual function of the first base class is called.

Guess you like

Origin blog.csdn.net/lxp_mujinhuakai/article/details/69787395