C++: virtual functions, virtual tables and pure virtual functions

1. Virtual function

(1) Definition:

If virtual is added before a member function, the member function is called a virtual function

(2) Virtual function rewriting (also called overwriting)

①The subclass defines a virtual function that is exactly the same as that in the parent class , then the subclass rewrites (or overrides) the virtual function of the parent class
②Identical means that the function name, return value, and formal parameter list are the same
③There is a This case is special: 协变, 协变which means that the return value of the virtual function in the subclass and the superclass is the subclass pointer and the superclass pointer respectively

//虚函数重写实例如下:
//完全相同,fun函数构成重写
class A
{
public:
    virtual void fun(int n)
    {
        printf("A::virtual void fun(int n)\n");
    }
private:
    int _a;
};
class B :public A
{
public:
    virtual void fun(int n)
    {
        printf("B::virtual void fun(int n)\n");
    }
private:
    int _b;
};
//协变
class A
{
public:
    virtual A* fun(int n)
    {
        printf("A::virtual void fun(int n)\n");
    }
private:
    int _a;
};
class B :public A
{
public:
    virtual B* fun(int n)
    {
        printf("B::virtual void fun(int n)\n");
    }
private:
    int _b;
};

2. Virtual table

(1) Definition:

The table that stores the address of the virtual function is called the virtual table. The address
here is not the real address of the function, but the jump address of the call instruction , which will be explained later, first as a simple understanding

(2) The essence of virtual table

The virtual table is a data structure, it is essentially an array of pointers , which stores the address of the virtual function, and its last element stores 0

(3) Use the memory window to verify the existence and structure of the virtual table

#include<iostream>
using namespace std;

class A
{
public:
    A()
        :_a(1)
    {}
    virtual void fun(int n)
    {
        printf("A::virtual void fun(int n)\n");
    }
private:
    int _a;
};

int main()
{
    A a;
    printf("%p\n", &a);
    printf("sizeof(A):%d\n",sizeof(A));
    return 0;
}

write picture description here
write picture description here

(4) Use the function to print the virtual table to verify the existence and structure of the virtual table

#include<iostream>
using namespace std;

typedef void(*VFunc)();//函数指针

class A
{
public:
    virtual void fun()
    {
        printf("A::virtual void fun(int n)\n");
    }
private:
    int _a;
};

void PrintVTable(int* vtable)
{
    int i = 0;
    //将虚函数表的内容打印,除最后一个元素
    //并将每个元素(即虚函数指针)通过VFunc调用一次
    for (i = 0; vtable[i] != 0; i++)
    {
        printf("vtable[%d]:%p\n", i, vtable[i]);
        VFunc f = (VFunc)vtable[i];
        f();
    }
    //验证虚函数表的最后一个元素为0
    printf("vtable[%d]:%p\n", i, vtable[i]);
}

int main()
{
    A a;
    PrintVTable((int*)(*(int*)&a));
    return 0;
}

write picture description here

(5) Summary: object structure and virtual table diagram

write picture description here

3. The situation of virtual table in inheritance

(1) Single inheritance: the situation that does not constitute overriding

#include<iostream>
using namespace std;

class A
{
//成员都设为public
public:
    virtual void fun1()//函数名为fun1,与类B中的不同,不构成重写
    {
        printf("A::virtual void fun(int n)\n");
    }
    int _a;
};

class B :public A
{
//成员都设为public
public:
    virtual void fun2()//函数名为fun2,与类A中的不同,不构成重写
    {
        printf("B::virtual void fun(int n)\n");
    }
    int _b;
};
int main()
{
    A a;
    B b;
    a._a = 1;
    b._a = 2;
    b._b = 3;
    printf("%p\n", &a);
    printf("%p\n", &b);
    return 0;
}

write picture description here
write picture description here
write picture description here

(2) Single inheritance: the case of constituting overriding

#include<iostream>
using namespace std;

class A
{
public:
    virtual void fun1()//函数fun1与类B中的完全相同,构成重写
    {
        printf("A::virtual void fun(int n)\n");
    }
    int _a;
};

class B :public A
{
public:
    virtual void fun1()//函数fun1与类A中的完全相同,构成重写
    {
        printf("B::virtual void fun(int n)\n");
    }
    int _b;
};

void PrintVTable(int* vtable)
{
    int i = 0;
    for (i = 0; vtable[i] != 0; i++)
    {
        printf("vtable[%d]:%p\n", i, vtable[i]);
        VFunc f = (VFunc)vtable[i];
        f();
    }
    printf("vtable[%d]:%p\n", i, vtable[i]);
}

int main()
{
    A a;
    B b;
    a._a = 1;
    b._a = 2;
    b._b = 3;
    printf("%p\n", &a);
    printf("%p\n", &b);
    return 0;
}

write picture description here
write picture description here
write picture description here

(3) Multiple inheritance: non-diamond inheritance

#include<iostream>
using namespace std;

class A
{
public:
    virtual void fun1()
    {
        printf("A::virtual void fun(int n)\n");
    }
    int _a;
};

class B
{
public:
    virtual void fun2()
    {
        printf("B::virtual void fun(int n)\n");
    }
    int _b;
};

class C:public A,public B
{
public:
    int _c;
};

int main()
{
    A a;
    B b;
    C c;
    a._a = 1;
    b._b = 2;
    c._a = 3;
    c._b = 4;
    c._c = 5;
    printf("%p\n", &a);
    printf("%p\n", &b);
    printf("%p\n", &c);
    return 0;
}

write picture description here
write picture description here
write picture description here

(4) Multiple inheritance: diamond inheritance

#include<iostream>
using namespace std;
class A
{
public:
    virtual void fun1()
    {
        printf("A::virtual void fun(int n)\n");
    }
    int _a;
};

class B:virtual public A//虚继承
{
public:
    virtual void fun2()
    {
        printf("B::virtual void fun(int n)\n");
    }
    int _b;
};

class C :virtual public A//虚继承
{
public:
    virtual void fun3()
    {
        printf("C::virtual void fun(int n)\n");
    }
    int _c;
};

class D :public B, public C
{
public:
    int _d;
};

int main()
{
    A a;
    B b;
    C c;
    D d;
    a._a = 1;
    b._a = 2;
    b._b = 3;
    c._a = 4;
    c._c = 5;
    d._a = 6;
    d._b = 7;
    d._c = 8;
    d._d = 9;
    printf("%p\n", &a);
    printf("%p\n", &b);
    printf("%p\n", &c);
    printf("%p\n", &d);
    return 0;
}

write picture description here

4. Summary/Summary of "The Situation of Virtual Tables in Inheritance"

1. Single inheritance: does not constitute rewriting
. The parent class and the subclass each have a virtual table, and the virtual table of the subclass includes all the pointers in the virtual table of the parent class.
2. Single inheritance: constitutes rewriting
. A virtual table, the pointers in the virtual table of the subclass are not the same as those in the virtual table of the parent class.
This is also the reason why polymorphism can determine the member virtual function to be called according to the type of the object pointed to by the pointer
. 3. Multiple inheritance: non-diamond inheritance
① If If the parent class has a virtual table, the child class will inherit the virtual table of all parent classes, and, whichever parent class is inherited first, the virtual function pointer of the child class will be placed in the virtual table corresponding to the parent class.
② Consider overriding 4. Multiple inheritance
: diamond inheritance
(1) virtual function virtual inheritance
If it is virtual inheritance, the content of the virtual table of the subclass only contains its own virtual function pointer , put the virtual function pointer of the parent class in the public area, and put the address of this public area in the object
(2) Diamond inheritance
① There are several "parent classes containing virtual tables", and there are several subclasses Each virtual table
② has several "virtual inheritance of the parent class of the same class", and the subclass has several virtual base table pointers
③ The subclass also contains a "pointer to the public area generated by virtual function virtual inheritance"

5. Pure virtual functions

(1) Definition

Assign 0 after the virtual function , eg: virtual void fun()=0;//只声明不实现
the virtual function is called a pure virtual function

(2) Abstract class/interface class

Classes containing pure virtual functions are called abstract classes or interface classes, and abstract classes/interface classes cannot instantiate objects .

(3) The meaning of abstract class/interface class

The existence of abstract classes makes subclasses must rewrite pure virtual functions. Only after rewriting pure virtual functions can subclasses instantiate objects.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324729121&siteId=291194637