【C++】继承与多态之虚函数

一、定义
虚函数必须是基类的非静态成员函数,其访问权限可以是private或protected或public,在基类的类定义中定义虚函数的一般形式。
虚函数是一种在基类定义为virtual的函数,并在一个或多个派生类中再定义的函数。实现多态性。虚函数的特点是,只要定义一个基类的指针,就可以指向派生类的对象。
二、具体实现
我们先看一段简单的代码:

#include <iostream>
using namespace std;

class Base
{
public:
	Base(int a) :ma(a)
	{
		cout << "Base::Base()" << endl;
	}
	~Base()
	{
		cout << "Base::~Base()" << endl;
	}
	void Show()
	{
		cout << "Base::Show()" << endl;
		cout << "ma"<< ma <<endl;
	}
protected:
	int ma;
};
class Derive : public Base
{
public:
	Derive(int b) :mb(b),Base(b)
	{
		cout << "Derive:: Derive()"<< endl;
	}
	~Derive()
	{
		cout << "Derive::~Derive()"<< endl;
	}
	void Show()
	{
		cout << "Derive::Show()" << endl;
		cout << "ma"<< ma <<endl;
		cout << "mb"<< mb <<endl;
	}
private:
	int mb;
};
int main()
{
	cout << sizeof(Base) <<endl;
	cout << sizeof(Derive) <<endl;

	Base* pb = new Derive(10);//基类的指针指向了派生类的对象
	cout << typeid(pb).name() << endl;
	cout << typeid(*pb).name() << endl;
	pb->Show();
	return 0;
}

这个代码的运行结果是:
在这里插入图片描述
现在我们在基类函数中加入virtual关键字:

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

加上关键字后我们发现运行的结果发生是这样的:
在这里插入图片描述
和我们之前完全不一样:首先基类中多出了四个字节,派生类中也多出了四个字节。然后解引用的时候发现打印的是派生类。而且调用的是派生类下的show函数。但为什么一加virtual关键字后会发生这么大的变化呢?
我们先看一下基类的内存布局:
在这里插入图片描述
我们发现加上虚函数之后会出现一个虚函数指针并且指向了一个虚函数表
1、首先我们先分析他为什么多出了四个字节
我们发现多了四个字节,也就是多了一个指针。既然基类中有虚函数,如果它有虚函数证明它就要有虚函数表,那么在这个对象的内存里面也就应该有虚函数指针来指向这个虚函数表,所以有了函数指针他也就多了四个字节。那么为什么派生类中也多出了四个字节呢?
我们首先要知道一个规定:基类中是虚函数派生类中同名函数也是虚函数。那如果基类中的Show函数是虚函数,那么派生类中和它同名的函数Show函数也就会是虚函数函数,这样派生类中就会多出四个字节。
2、我们现在分析为什么解引用之后打印的却是class Derive
这个时候我们就需要知道它的虚函数表里存放的是什么
在这里插入图片描述
在这里插入图片描述
首先存放的是RTTI信息(runtime type infomation)
然后是偏移量(vfptr相对于整个内存布局的偏移),因为vfptr的优先级是最高的,所以它的偏移量一般是0
接下来第三个模块放的是虚函数的地址。
那为什么打印的是class Derive呢?
因为基类的虚函数表中的RTTI存放的是class Base。派生类的虚函数表中的RTTI存放的是class Derive。我们用了一个基类的指针指向了一个派生类的对象。

Base* pb = new Derive(10);

然后pb进行了解引用,那么它的本质也就成了派生类的对象,我们现在提取RTTI信息时就提取成了派生类的虚函数表的信息了,就成了class Derive
3、现在分析为什么调用的是Derive::Show
在这里插入图片描述
这是一个派生类的内存布局以及派生类对应的虚表。现在是用了一个基类的指针(占四个字节),那里面存放的就是派生类对象的地址,也就是pb指向了派生类对象。因为是一个基类指针,所以只能用基类的部分(图中红色的部分)。但这个基类中的vfptr指向的vftable是派生类的vftable。
现在如果再通过pb调用Show的话。需要先通过pb所指向的地址的vfptr找到Show函数的入口地址来调用,这个Show函数是派生类的Show函数。也就是为什么最后打印出来的Derive::Show(图中蓝色部分)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值