网站首页 > 技术文章 正文
通过指针、引用来调用一个虚函数,实际被调用的函数到运行时才能确定,似乎有一种神秘感,使得我们不能用思考普通函数的方式去思考虚函数。对于初学者来说,对虚函数保持这种神秘感倒也无妨,只要清楚它的用法就好了,但是若是想要“究其原理”也并不是一件困难的事情。
动态绑定的关键是,在运行时决定被调用的函数。
这个要求很容易让我们想起函数指针。一个函数指针可以被赋予不同函数的入口地址,如果通过函数指针去调用函数,实际被调用的函数一般到了运行的时候才能确定,因此通过函数指针去调用函数,也是一种动态绑定,是一种由源程序进行的显示控制的动态绑定。而虚函数的动态绑定,一些控制细节被隐藏了起来,由编译器自动处理了。
编译器实现虚函数的动态绑定的细节并没有在C++标准中规定,So会因编译器而异。
下面以下列代码为例,进行讨论
class Base{ //基类定义
public:
virtual void f(); //基类的虚函数f()
virtual void g(); //基类的虚函数g()
private:
int i; //基类的数据成员
};
class Derived:public Base{ //派生类定义
public:
virtual void f(); //覆盖基类的虚函数f()
virtual void h(); //声明派生类的新的虚函数h()
private:
int j; //派生类的数据成员
};
注:Base类和Derived类成员函数的实现略去。
首先介绍一种直接的处理方法:
在每个对象中,除了存储数据成员外,还为每个虚函数设置一个函数指针,分别存放这些虚函数对应的代码的入口地址。由于派生类也要继承这些虚函数的接口,因此也要保留这些指针,而把派生类中引入的新的数据成员和函数指针置于从基类继承下来的数据成员和函数指针后。在各个类的构造函数中,为各个函数指针初始化,使得基类对象中的函数指针指向为基类定义的函数,派生类对象中的函数指针指向派生类覆盖后的函数。在通过指针或引用进行函数调用时,先读取相应的函数指针,再通过函数指针调用相应的函数。(可以结合下图理解)
如图所示,由于Derived类覆盖了Base类的f 函数,因此Base对象和Derived对象中为f 函数设置的指针,指向不同的函数代码;Derived类未覆盖g函数,因此两个类的对象中为g函数设置的指针指向相同的代码;至于h函数,它是派生类新增的函数,只有Derived对象中有为它准备的指针。
这种方式存在一个致命的问题——占用的额外空间太大,例如每一个Base对象都要占用两个指针的额外空间,每个Derived 对象要占用3个指针的额外空间。所以这并不是被广泛采用的方法,之所以将它提出仅仅是因为它简单,直接,利于理解,只要在此基础上再深入一点便能得到更好的处理方法了。明天我们再来一起分析一下更好的处理方法。
猜你喜欢
- 2024-10-18 了解C语言中的操作符(c语言操作符怎么定义)
- 2024-10-18 20天轻松入门《C++第二章——程序设计基础》—3经坛教育
- 2024-10-18 C++中的volatile关键字(volatile关键字 c语言)
- 2024-10-18 C/C++软件开发证书怎么考?报考难度大吗?含金量高吗?
- 2024-10-18 c++数据类型(c++数据类型转换)
- 2024-10-18 C语言中实现边沿函数算法及应用,这是抛弃PLC留下的痛!
- 2024-10-18 C基础、经典问题:交换a、b值较好的方法?
- 2024-10-18 C++ 避免使用模块重新编译模板库(调用c++模块,不忽略异常)
- 2024-10-18 面试大厂c/c++后台开发岗,如何从技术层面上杀出重围?
- 2024-10-18 关于C语言交换两个数的实现方法以及个人心得
- 最近发表
- 标签列表
-
- cmd/c (64)
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- sqlset (64)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)