[C++] Polymorphic conclusion proof

foreword

At the end of the last blog on the underlying principles of polymorphism, I talked about a few summary knowledge points. This blog will provide some proofs for it. Without further ado, let’s start
today’s study immediately.

insert image description here

1. Virtual functions of subclasses

In the last blog, we talked about
summarizing the virtual table generation of subclasses: a 父类中的虚表内容拷贝一份到子类的虚表. b. If the subclass 重写has a virtual function in the parent class, use 子类自己的函数覆盖虚表中父类的虚函数c. The newly added virtual function of the subclass itself is 声明次序added 子类虚表的最后
here according to its subclass. We say that the subclass is added 新增加的虚函数according to its subclass声明次序子类虚表的最后

This is true, but this conclusion proves that the process is worth learning

Next, we try to prove it.
First, let's see if the monitoring window appears.
We define such a parent-child class

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

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


private:
	int _b = 1;
};

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

	virtual void Func4()
	{
    
    
		cout << "Derive::Func4()" << endl;
	}
private:
	int _b = 2;
};

int main()
{
    
    
	Base b;
	Derive d;

	return 0;
}

The parent class has two virtual functions, the subclass overrides the first one, and adds a virtual function by itself

1. Debug window

Next, we open the monitoring window to see if the new virtual function of the subclass is behind the virtual table of the subclass
insert image description here

We see that there are two virtual functions, Func1 and Func2, in the virtual function table of the parent class. Because Func1 is rewritten in the subclass, Func1 in the subclass is ; Func2 is not 函数内容不同,函数地址也不同rewritten 还是父类的虚函数. This is consistent with what we said above, the formation of subclass virtual tables.

But the monitoring window is not working 没有显示子类新增加的虚函数, we might as well take 内存窗口another look

insert image description here
We can see that the addresses of the first two function pointers can be corresponding, and in the third position, there is another address with a similar address. Could this be the function pointer of the virtual function newly added by the subclass? We can't draw conclusions just by looking at the memory table.

2. Print the vtable

In the monitoring window and the memory window, we cannot see whether the virtual functions newly added by the subclass have entered the end of the virtual table.

Because it is the function pointer stored in the virtual table, we can try 获取这些函数指针, and then 调用函数, for this, we write the corresponding in each virtual function 打印, when we call the third address, if the content of the new virtual function of the subclass is printed out, Doesn't that prove that the virtual function added by the subclass will be placed at the end of the virtual table?

First, we write a function to print the content of the virtual table, because the virtual table is an array of function pointers, we can rename the function pointers for easy reading. At the same time, the fourth position in the virtual table is a null pointer, so the termination condition is to access the null pointer
insert image description here

//函数指针
typedef void(*VF_PTR)();
//参数是函数指针数组
void Print(VF_PTR table[])
{
    
    
	//访问到空指针就终止循环,停止打印
	for (int i = 0; table[i]; i++)
	{
    
    
		printf("[%d]:%p\n", i, table[i]);
	}
}

Next, we have to think about how to obtain the virtual table
. We thought that the virtual table is in the first position of the object, because it is a pointer to an array of function pointers, and it is still a pointer in essence, so it occupies four bytes. So we can take the address, and then force it, so that the access size of the pointer is 4, and then force it into an array of function pointers, which can match the parameters of Print

(1). The first way of writing

//先取地址,再强转成访问4字节的int指针,解引用获取4个字节
//再强转成函数指针的指针,因为函数指针数组本质也是函数指针的指针
Print((VF_PTR*)(*(int*)(&d)));

insert image description here
But this method has limitations, because the access range of int* is 4 bytes, the regulation is dead, but under 64-bit, the pointer is 8 bytes, so it cannot run if it does not
insert image description here
match

(2). The second way of writing

can also
insert image description here

Here is a little explanation of the second way of writing
, because the Print parameter is VF_PTR [ ]an array of function pointers, and the essence is the same, 函数指针的指针
so we can also take the address of the object, force it into VF_PTR**a pointer to the pointer, 访问也是4字节and then dereference to get 4 bytes, and become VF_PTR*, Just match the parameters of Print

This method is compatible with 64 bits, because the access range of the pointer is used
insert image description here

(3). The third way of writing

Secondly, we can directly use void* instead of function pointers, because they are essentially addresses, so they can be replaced, but we need to use a first-level pointer instead of a function pointer, so it will eventually become a third-level pointer, which is not recommended

void Print(void **table)
{
    
    
	for (int i = 0; table[i]; i++)
	{
    
    
		printf("[%d]:%p\n", i, table[i]);
	}
}

insert image description here
The basic principle is the same as the second method, and it is also compatible with 64-bit

3. Proof

After obtaining the virtual table, because the virtual table contains function pointers, we can use these function pointers to call the corresponding functions

typedef void(*VF_PTR)();
void Print(VF_PTR table[])
{
    
    
	for (int i = 0; table[i]; i++)
	{
    
    
		printf("[%d]:%p\n", i, table[i]);
		//取出函数指针
		VF_PTR fun = table[i];
		//调用
		fun();
	}
}

insert image description here

We see that Func4 is indeed added to the end of the subclass virtual table

2. Virtual table pointer initialization

The virtual table is 编译generated at the stage. Once we debug, the virtual table already exists,
insert image description here
but the virtual table pointer is not initialized at this time, so when is it initialized?

虚表指针的初始化is actually 初始化列表going on
insert image description here

3. Storage of virtual tables

We also said in the last blog, 虚表是存储在常量区,也就是代码段. Next, we simply prove that

insert image description here

We create variables at various locations and print their addresses, and find that the address of the data in the constant area is closest to the location of the virtual table, so虚表是在常量区

conclusion

If you think this article is helpful to you, you might as well like it to support the blogger, please, this is really important to me.
insert image description here

Guess you like

Origin blog.csdn.net/m0_72563041/article/details/129959800