博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++虚函数原理
阅读量:4090 次
发布时间:2019-05-25

本文共 1560 字,大约阅读时间需要 5 分钟。

一、前言

C++的特性使得我们可以使用函数继承的方法快速实现开发,而为了满足多态(Polymorphism)这一性质,C++允许用户使用虚函数 (virtual function) 来完成 运行时决议 这一操作,这与一般的 编译时决定 有着本质的区别。多态的目的,就是要让处理“基类之对象”的程序代码,能完全无障碍的处理“派生类之对象”。如果基础类和派生类定义了虚函数,那么通过对象指针调用成员函数时,该函数是根据指针实际指向的对象类型确定。

二、虚函数表

对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。 在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其内容真实的反应实际的函数。这样,在有虚函数的类的实例中,这个表被分配在了 这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。

这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

举个例子,定义三个类,A,B,C依次继承

class ClassA{public:	int m_data1;	int m_data2;	void func1() {}	void func2() {}	virtual void vfunc1() {}	virtual void vfunc2() {}};class ClassB:public ClassA{public:	int m_data3;	void func2() {}	virtual void vfunc2() {}};class ClassC :public ClassB{public:	int m_data1;	int m_data4;	void func2() {}	virtual void vfunc1() {}	};

那么它们的虚函数表为

三、构造函数和析构函数能否为虚函数?

(1) 构造函数不能为虚函数

C++对象在三个地方构建:(1)函数堆栈;(2)自由存储区,或称之为堆;(3)静态存储区。无论在哪里构建,其过程都是两步:首先,分配一块内存;其次,调用构造函数。好,问题来了,如果构造函数是虚函数,那么就需要通过vtable 来调用,但此时面对一块 raw memeory,到哪里去找 vtable 呢?毕竟,vtable 是在构造函数中才初始化的啊,而不是在其之前。因此构造函数不能为虚函数。

(2)析构函数可以是虚函数,且常常如此

这个就好理解了,因为此时 vtable 已经初始化了;况且我们通常通过基类的指针来销毁对象,如果析构函数不为虚的话,就不能正确识别对象类型,从而不能正确销毁对象。

(3)C++使用虚函数的时候,子类也要使用virtual关键字吗

父类使用虚函数是为了让子类重写,那子类重写的时候也需要带virtual关键字吗?比如:

class Base{
virtual bool init();
};
class Derived{
virtual bool init(); //这里的vitual是必须的吗?好像不用也能编译通过呃…..
};

C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此,在子类从新声明该虚函数时,可以加,也可以不加,但习惯上每一层声明函数时都加virtual,使程序更加清晰。

参考:

 

转载地址:http://xziii.baihongyu.com/

你可能感兴趣的文章
MySQL DDL
查看>>
IDEA properties文件中文显示异常
查看>>
Maven POM packaging
查看>>
centos docker阿里云镜像配置
查看>>
IDEA 2020 导入maven项目spring等jar找不到
查看>>
vagrant 常用指令手册
查看>>
windows 10 9001端口被占用
查看>>
postman online/web 在线工具
查看>>
vscode 创建vue自定义模板
查看>>
spring cloud gateway CORS配置
查看>>
Java -运算符+引起的NullPointerException
查看>>
spring cloud gateway routes加载顺序的研究
查看>>
MyBatis 大于小于不等于的写法
查看>>
正则表达式具体示例
查看>>
Mysql Json操作
查看>>
Fiddler4 抓取Chrome浏览器的Http(s)
查看>>
vue ReferenceError: PubSub is not defined 解决方案
查看>>
Springboot 启动报错 maven-surefire-plugin
查看>>
RabbitMQ 开发时指定消息消费者的方式
查看>>
centos 安装MySQL8.0
查看>>