The principle of polymorphism, the virtual function table of single inheritance and multiple inheritance, and the printing of virtual function table.

1. The principle of polymorphism

1. What is the following result?

class A
{
public:
virtual void func()
{
cout << "func()" << endl;
}

private:
int_a = 1;
};

int main()
{
printf("%d\\
", sizeof(A));
return 0;
}

Is it 4? 8? or how much? The printed results are as follows:

Why 8? , take a look through debugging, as follows:

Passed debugging It can be seen that there are not only _a members, but also a member __vfptr, which is the virtual function table pointer. Why is there a virtual function table pointer? Because the address of the virtual function must be stored in the virtual function table! And there must be at least one virtual function table pointer in the class containing virtual functions!

2. Modify the above code as follows

class A
{
public:
virtual void Func1()
{
cout << "A::Func1()" << endl;
}

virtual void Func2()
{
cout << "A::Func2()" << endl;
}

void Func3()
{
cout << "A::Func3()" << endl;
}
private:
int_a = 1;
};

class B : public A
{
public:
virtual void Func1()
{
cout << "B::Func1()" << endl;
}
private:
int_b = 2;
};

int main()
{
A a;
B b;
return 0;
}

Debug to see what the internal structure is, as follows:

Through the above debugging, you can see:

(1) There is also a virtual table pointer in the derived class object d. The d object is composed of two parts, one part is a member inherited from the parent class, and the virtual table pointer is the existing part, and the other part is its own member.

(2) The virtual tables of base class b objects and derived class d objects are different. Here we find that Func1 has been rewritten, so the rewritten Derive::Func1 is stored in the virtual table of d, so the rewritten virtual function Writing is also called coverage, and coverage refers to the coverage of virtual functions in the virtual table. Rewriting is called syntax, and coverage is called principle layer.

(3) In addition, Func2 is a virtual function after being inherited, so it is put into the virtual table, and Func3 is also inherited, but it is not a virtual function, so it will not be put into the virtual table.

(4) The essence of the virtual function table is an array of pointers storing virtual function pointers. Generally, a nullptr is placed at the end of this array.

(5) Summarize the virtual table generation of derived classes:

a. First copy the content of the virtual table in the base class to the virtual table of the derived class

b. If the derived class rewrites a virtual function in the base class, overwrite the virtual function of the base class in the virtual table with the derived class’s own virtual function

c. The newly added virtual functions of the derived class are added to the end of the derived class virtual table according to their declaration order in the derived class.

(6) So where does the virtual function exist? Where does the virtual table exist?
Does a virtual function exist in a virtual table? Does the virtual table exist in the object?
wrong! ! ! The virtual table stores virtual function pointers, not virtual functions. Virtual functions are the same as ordinary functions, and they all exist in the code segment, but their pointers are stored in the virtual table. In addition, what is stored in the object is not a virtual table, but a virtual table pointer. So where does the virtual table exist? In fact, if we verify it, we will find that there is a code segment under vs.

Through the above example, let’s take a formal look at the principle of polymorphism! !

The previous article talked about the polymorphic conditions. When the polymorphic conditions are met, different calls will be completed when passed to the parent class pointer or reference. Then let me explain why, as follows:

class Person {
public:
virtual void BuyTicket() { cout << "Buy ticket full price" << endl; }
};
 
class Student : public Person
{
public:
virtual void BuyTicket() { cout << "Buy Ticket Half Price" << endl; }
};
 
void Func(Person & p)
{
p. BuyTicket();
}
 
int main()
{
Person q;
Func(q);

Student st;
Func(st);
 
return 0;
}

Because when q is passed to p for reference, p points to the q object, so slicing occurs, but because they are all of the Person class, so their own method is called.

When st is passed to p for reference, p points to the st object, and student inherits the Person class, and rewrites the virtual function to form polymorphism, but the virtual function in the st object is rewritten! Instead of inherited virtual functions! You can debug and check it out, as follows:

Although slicing occurs, p points to the address in front of Person in the st object, but because the method is rewritten, the method of student is called!

Take a look at the assembly code below, as follows:

When calling in polymorphism, the register is called! The specific details will not be discussed.

In short, when polymorphic calls are made, the virtual function address is found in the virtual table pointing to the object at runtime, and the call is made!

So what does not constitute polymorphism?

class Person {
public:
virtual void BuyTicket() { cout << "Buy ticket full price" << endl; }
};

class Student : public Person
{
public:
//virtual void BuyTicket() { cout << "Buy Ticket Half Price" << endl; }
virtual void Buy() { cout << "Half price for tickets" << endl; }
};

void Func(Person & p)
{
p. BuyTicket();
}

int main()
{
Person q;
Func(q);

Student st;
Func(st);

return 0;
}

If I destroy a condition at will, it does not constitute polymorphism, so what is it like when calling it? as follows:

You can see that the call is directly the function address!

Because polymorphism is not satisfied, it is an ordinary function call. When compiling and linking, the function address is confirmed, and it is called directly at runtime.

Second, virtual function table of single inheritance and multiple inheritance.

// single inheritance
// print virtual function table
class Person
{
public:
virtual void BuyTicket()
{
cout << "Person::BuyTicket()" << endl;
}

virtual void func_person()
{
cout << "Person::func_person()" << endl;
}
};

class Student : public Person
{
public:
virtual void BuyTicket()
{
cout << "Student::BuyTicket()" << endl;
}

// View the virtual table of s in the monitoring window, but the func_student() function cannot be found in it
virtual void func_student()
{
cout << "Person::func_student()" << endl;
}
};


typedef void(*VFTABLE)();

void PRINT_VFTABLE(VFTABLE table[], int n)
{
//for (int i = 0; table[i] != nullptr; i ++ ) // vs gives 0 by default later; so you can use table[i] != nullptr; linux needs to display, write and print a few
for (int i = 0; i < n; i ++ )
{
printf("table[%d]->%p::", i, table[i]);

VFTABLE pf = table[i];
pf(); // function pointer + () calls its corresponding function
}

cout << endl;
}


//int main()
//{
// Student s;
//
// Person p;
//
// //PRINT_VFTABLE((VFTABLE*)*(int*) & amp;s);
// PRINT_VFTABLE((VFTABLE*)*(int*) & amp;s, 3); // There are 3 subclasses, 3 are displayed for writing and printing
// PRINT_VFTABLE((VFTABLE*)*(int*) & amp;p, 2); // parent class has 2, display write and print 2
//
// return 0;
//}



// multiple inheritance
// print virtual function table


class Base1
{
public:
virtual void func1()
{
cout << "Base1::func1()" << endl;
}

virtual void func_Base1()
{
cout << "Base1::func_Base1()" << endl;
}
};


class Base2
{
public:
virtual void func1()
{
cout << "Base2::func1()" << endl;
}

virtual void func_Base2()
{
cout << "Base2::func_Base2()" << endl;
}
};


class Base3 : public Base1, public Base2
{
public:
virtual void func1()
{
cout << "Base3::func1()" << endl;
}

virtual void func_Base3()
{
cout << "Base3::func_Base3()" << endl;
}
};

typedef void(*VFTABLE)();

void PRINT_VFTABLE(VFTABLE table[])
{
for (int i = 0; table[i] != nullptr; i ++ )
{
printf("table[%d]->%p->", i, table[i]);

VFTABLE pf = table[i];
pf();
}
cout << endl;
}

int main()
{
Base3 b3;
Base1 b1 = b3;
Base2 b2 = b3;

PRINT_VFTABLE((VFTABLE*)*(int*) &b1);
PRINT_VFTABLE((VFTABLE*)*(int*) &b2);
PRINT_VFTABLE((VFTABLE*)*(int*) &b3);

return 0;
}

A function is written above to print the virtual function table, you can try to debug and print it.