9-3:C++多态之多态的实现原理之虚函数表,虚函数表指针静态绑定和动态绑定

Posted 快乐江湖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9-3:C++多态之多态的实现原理之虚函数表,虚函数表指针静态绑定和动态绑定相关的知识,希望对你有一定的参考价值。

(1)虚函数表

如下代码,计算此带有虚函数的类的大小

#include <iostream>
using namespace std;

class Test
{
public:
	virtual void fun1()
	{
		cout << "fun(1)" << endl;
	}
	virtual void fun2()
	{
		cout << "fun(2)" << endl;
	}
	void fun3()
	{
		cout << "fun(3)" << endl;
	}
private:
	int _num = 1;
};

int main()
{
	Test t;
	cout << sizeof(t) << endl;

}

其结果并不是4,而是8
在这里插入图片描述
启动调试后发现,除了一个成员外,似乎还多了一个_vfptr,很明显它是一个指针,我们称这个指针为虚函数表指针,它指向了一个虚函数表,这个虚函数表就是一个数组,这个数组中的每个元素都是一个虚函数指针
在这里插入图片描述
关于虚函数表及虚函数表指针以下几点需要注意

  1. 这里的虚函数表和前面的虚基表不要搞混,前面的虚基表存储的是偏移量,这里的虚函数表存储的是虚函数指针
  2. 不同对象的虚函数表是一样的
  3. 虚函数表指针在代码段
    在这里插入图片描述

如下的继承体系中,子类重写了虚函数fun1

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

class Test
{
public:
	virtual void fun1()
	{
		cout << "fun(1)" << endl;
	}
	virtual void fun2()
	{
		cout << "fun(2)" << endl;
	}
	void fun3()
	{
		cout << "fun(3)" << endl;
	}
private:
	int _num = 1;
};

class Test2 : public Test
{
public:
	virtual void fun1()
	{
		cout << "Test2:fun1" << endl;
	}
private:
	int _num2 = 2;

};

int main()
{
	Test t;
	Test2 t2;

}

透过监视窗口可以看出,由于子类重写了fun1,因此子类的虚函数表中的第一个虚函数指针发生了变化,而fun2没有被重写,因此还是使用的是相同的
在这里插入图片描述

(2)多态原理

如下继承体系中,fun1和fun2重写,使用FUN函数,以父类指针做形参,分别调用fun1和fun2,因此构成多态

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

class Test
{
public:
	virtual void fun1()
	{
		cout << "fun(1)" << endl;
	}
	virtual void fun2()
	{
		cout << "fun(2)" << endl;
	}
	void fun3()
	{
		cout << "fun(3)" << endl;
	}
private:
	int _num = 1;
};

class Test2 : public Test
{
public:
	virtual void fun1()
	{
		cout << "Test2:fun1" << endl;
	}
	virtual void fun2()
	{
		cout << "Test2:fun2" << endl;
	}
private:
	int _num2 = 2;

};

void FUN(Test* p)
{
	p->fun1();
	p->fun2();
}

int main()
{
	Test t;
	Test2 t2;
	FUN(&t);
	FUN(&t2);
}

通过,汇编代码可以发现多态的实现原理。
首先是父类指针指向父类对象,分别调用fun1和fun2,原理如图
在这里插入图片描述
前面说过,如果将形参换成父类的对象,而不采用指针或引用,那么就不会构成多态,因此在这样的情况下,调用谁的函数已经早就在编译的时候就确定好了
在这里插入图片描述

(3)静态多态与动态多态

A:静态多态

静态多态其实我们已经用过很多次了,甚至在第一次学习C++时,就遇到过了

#include <iostream>
using namespace std;
int main()
{
	int i=0;
	double d=3.14;
	cout<<i;
	cout<<d;
}

为什么同一个cout可以输出不同类型的变量——函数重载。所以函数重载就属于一种静态的多态,静态多态又称为静态绑定,之所以是静态的是因为在程序编译时就已经确定了行为。比如前面的父类对象作为形参那个例子,编译器已经知道了一定会调用父类的函数

B:动态多态

动态多态又称为动态绑定,就像前面的例子中:在程序运行时,会根据具体拿到的类型执行相应的动作。

以上是关于9-3:C++多态之多态的实现原理之虚函数表,虚函数表指针静态绑定和动态绑定的主要内容,如果未能解决你的问题,请参考以下文章

多态实现之虚函数

C++之虚函数和多态

C++之多态总结(多态的定义及实现,抽象类,多态原理,单继承,多继承中的虚函数表)

48.C#--多态之虚方法

C++面试题之虚函数(表)实现机制

C++ 面向对象程序三大特性之 多态