C++——“多态”的底层实现,看了之后保你理解

发布时间:2023-03-29 17:00

作者:小 琛
欢迎转载,请标明出处

先来看一道题目:
求代码中输出值为多少?

#include 
class text
{
public:
	virtual void Func1()
	{
		std::cout << \"Func1()\" << std::endl;
	}
private:
	int _b = 1;
};
int main()
{
	text temp;
	std::cout << sizeof(temp) << std::endl;
	return 0;
}

很多人在这里都会犯错,回答成4,其实结果如下:
\"C++——“多态”的底层实现,看了之后保你理解_第1张图片\"
这是为什么呢?

我们通过调试,借助变量窗口来分析
\"C++——“多态”的底层实现,看了之后保你理解_第2张图片\"
可以看到,在temp对象中,存在两个成员,一个是本事定义的变量int _b,而另一个变量_vfptr,是一个\"数组指针”,我们并没有定义它,而它却存在于对象中!

重要结论!!!
也就是说,虚函数在类中的存储是通过一个指针,该指针指向一个数组,对应的虚函数指针分别存放于该数组中。我们将这个指针称之为虚表指针

我们大胆猜想,是否和虚函数的定义有关呢?

接着,我们写一份完整的多态代码。

#include 
using namespace std;
class Base
{
public:
	virtual void Func1()
	{
		cout << \"Base::Func1()\" << endl;
	}
	virtual void Func2()
	{
		cout << \"Base::Func2()\" << endl;
	}
	void Func3()
	{
		cout << \"Base::Func3()\" << endl;
	}
private:
	int _b = 1;
};
class Derive : public Base
{
public:
	virtual void Func1()
	{
		cout << \"Derive::Func1()\" << endl;
	}
private:
	int _d = 2;
};
void function(Base& v)
{
	v.Func1();
}
int main()
{
	Base b;
	Derive d;

	function(b);
	function(d);
	return 0;
}

再次借助调试窗口来分析:
\"C++——“多态”的底层实现,看了之后保你理解_第3张图片\"
看图中的那么数组指针,该指针指向的数组的内容在监视窗口也可以看到,注意看下图中红色圈起部分
\"C++——“多态”的底层实现,看了之后保你理解_第4张图片\"
我们发现它们内容是不同的,父类型b中存放的是基类Base的两个成员函数的函数指针,分别为Func1于Func2,但派生类d中存储的是从父类继承来的Func2与derive本身重写的的Func1

总结以下几点

  1. 派生类对象d中也有一个虚表指针。d对象由两部分构成,一部分是父类继承下来的成员,另一部分是自身的成员。虚表指针指向的数组成员也是如此。但又有不同!
  2. 基类b对象和派生类d对象虚表是不一样的,**我们对Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,**覆盖就是指虚表中虚函数的覆盖。另外Func2继承下来后是虚函数,所以放进了虚表。Func3也继承下来了,但是不是虚函数,所以不会放进虚表。
  3. 虚函数表本质是一个存虚函数指针的指针数组,这个数组最后面放了一个nullptr。

我们再次回顾多态的构成条件:

  1. 被调用函数必须是虚函数,且该虚函数被子类继承,同时要被重写。
  2. 必须使用引用或者指针来调用虚函数。

这样就不难理解其中的道理了,因为要完成虚表中的覆盖,则派生类就必须对虚函数进行重写

笔者来对以上内容进行总结:

虚函数在类中的存放,就是在这样的类中,会生成一个指针,该指针是一个数组指针,我们通常将这个数组成为虚表,虚表内存放的则是这些虚函数的函数指针。
而在实现多态的时候,派生类继承父类,同样会有一个数组指针,这个指针所指向的数组(虚表)存放的内容分为两部分:一是自己本身的虚函数,二是从父类继承来的。但从父类继承来的部分虚函数会因为实现多态而被我们重写,因此进行覆盖掉。
所产生的结果就是父类的虚表和子类的虚表内容并不相同。 而我们通过指针或者引用进行不容对象的调用的时候,不同的对象会在虚表内找到相应的函数指针从而执行,和上面例子相匹配的就是b会调用父类的Func1(),而d会调用派生类的Func1(),以此达到多态!
\"C++——“多态”的底层实现,看了之后保你理解_第5张图片\"

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号