虚函数表是在什么时期建立的?

  最近参加阿里巴巴公司的内推,面试官问了“虚函数表是在什么时期建立的?”。因为以前对虚函数表的理解不够多,所以就根据程序构建(Build)的四个过程(预编译、编译、汇编和链接),推导出虚函数表应该是在编译期确定的,原因如下:

  1)预编译器主要处理那些源代码文件中的以“#”开始的预编译指令,如“#include”、“#define”。很明显这个过程可以排除。

  2)汇编器是将编译器生成的汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。汇编过程相对于编译期来说比较简单,没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译就行了。所以,汇编期也是可以排除的。

  3)链接器(现只考虑静态链接)是将汇编器生成的目标文件(和库)链接成一个可执行文件,本质上做的是重定位(Relocation)的工作,详细可参考《程序员的自我修养》2.3、2.4节。很明显链接期也是可以排除的。

  4)编译器要做的事情就比较多了,包括词法分析、语法分析、语义分析及优化代码等,是整个程序构建的核心。所以,排除了预编译期、汇编期、链接期及考虑到编译期所做的事情,虚函数表应该是在编译期建立的。

  

  上边给出的答案还是有点不够全面,因为忽略了动态链接。不过,我们在《深度探索C++对象模型》的4.2节能够找到完美答案,具体摘抄如下:

  “表格中的virtual functions地址是如何被建构起来的?在C++中,virtual functions(可经由其class object被调用)可以在编译时期获知。此外,这一组地址是固定不变的,执行期不可能新增或替换之。由于程序执行时,表格的大小和内容都不会改变,所以其建构和存取皆可以由编译器完全掌控,不需要执行期的任何介入。”

虚函数表

   C++中的虚函数的作用主要是实现了多态机制,即父类类别的指针(或者引用)指向其子类的实例,然后通过父类的指针(或者引用)调用实际子类的成员函数。多态机制可以简单地概括为“一个接口,多种方法”。

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

  下边我们通过一个小程序来看看虚函数表到底是怎么样的?

 #include <iostream>
using namespace std; class Base
{
public:
virtual void fn() { cout << "In Base class" << endl; }
};
class Sub :public Base
{
public:
virtual void fn() { cout << "In Sub class" << endl; }
}; void main()
{
Base bc;
Sub sc;
}

  对这个程序调试,截图如下:

  

  由上图可知,虚函数表_vfptr已经将父类Base和子类Sub的相同函数fn动态绑定到对应的类上,而且地址不一样。

有关多态的值得注意的例子

不用多态

  先看第一个程序:

 #include <iostream>
using namespace std; class Base
{
public:
void fn() { cout << "In Base class" << endl; }
};
class Sub :public Base
{
public:
void fn() { cout << "In Sub class" << endl; }
}; void test(Base& b)
{
b.fn();
} void main()
{
Base bc;
Sub sc;
test(bc);
test(sc);
}

  函数输出如下:

  

  Sub对象sc在传递给test函数时,其另外添加的方法成员等(Sub)会被截掉,只剩Base部分,所以输出是In Base class而不是In Sub class。

利用多态

  具体程序如下:

 #include <iostream>
using namespace std; class Base
{
public:
virtual void fn() { cout << "In Base class" << endl; }
};
class Sub :public Base
{
public:
virtual void fn() { cout << "In Sub class" << endl; }
}; void test(Base& b)
{
b.fn();
} void main()
{
Base bc;
Sub sc;
test(bc);
test(sc);
}

  程序输出如下:

  

  这下程序就正确了。

  其实还有另一种方法可以达成跟虚函数一样的效果,不过这并不是一种好的做法:

 #include <iostream>
using namespace std; class Base
{
public:
void fn() { cout << "In Base class" << endl; }
};
class Sub :public Base
{
public:
void fn() { cout << "In Sub class" << endl; }
}; void main()
{
Base bc;
Sub sc;
bc.fn();
sc.fn();
}

  具体输出如下:

  

参考资料

  《深度探索C++对象模型》

  《程序员的自我修养》

C++中的虚函数表是什么时期建立的?的更多相关文章

  1. C++中的虚函数表

    (感谢http://blog.csdn.net/haoel/article/details/1948051/) C++中的虚函数的作用主要是实现了多态的机制. 多态,简而言之就是用父类型别的指针指向其 ...

  2. (C/C++学习)4.C++类中的虚函数表Virtual Table

    说明:C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为V-Table.在这个表中,主要为一个类的虚函数的地址表,这张表解决了继承.覆写的问题,保证其真实反应实际的虚函数调用 ...

  3. C++ 中的虚函数表及虚函数执行原理

    为了实现虚函数,C++ 使用了虚函数表来达到延迟绑定的目的.虚函数表在动态/延迟绑定行为中用于查询调用的函数. 尽管要描述清楚虚函数表的机制会多费点口舌,但其实其本身还是比较简单的. 首先,每个包含虚 ...

  4. C++虚函数表解析(图文并茂,非常清楚)( 任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法)good

    C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有“多种形态”,这是一种泛型技术 ...

  5. C++ 虚函数表解析

    转载:陈皓 http://blog.csdn.net/haoel 前言 C++中 的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实 ...

  6. C++虚函数表原理

    C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指 向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有“多种形态”,这是一种泛型技 ...

  7. C++ 虚函数表解析(转载)

    转载自:陈皓 http://blog.csdn.net/haoel/article/details/1948051/ 前言 C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型 ...

  8. 转载:C++ 虚函数表解析

    目录(?)[+]   转载:http://blog.csdn.net/haoel/article/details/1948051# 前言 C++中 的虚函数的作用主要是实现了多态的机制.关于多态,简而 ...

  9. C++虚函数表解析(转)

    C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有“多种形态”,这是一种泛型技术 ...

随机推荐

  1. Android自定义View(LimitScrollerView-仿天猫广告栏上下滚动效果)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53303872 本文出自:[openXu的博客] 1分析 2定义组合控件布局 3继承最外层控件 ...

  2. Scala:集合类型Collection和迭代器

    http://blog.csdn.net/pipisorry/article/details/52902549 Scala Collection Scala 集合分为可变的和不可变的集合. 可变集合可 ...

  3. Html书写规范,基本标签使用

    一.html简介1.html是什么Html是用来描述网页的一种语言.(1)HTML 指的是超文本标记语言 (Hyper Text Markup Language)(2)HTML 不是一种编程语言,而是 ...

  4. 剑指Offer——咪咕笔试题+知识点总结

    剑指Offer--咪咕笔试题+知识点总结 情景回顾 时间:2016.10.09 15:00-16:30 地点:山东省网络环境智能计算技术重点实验室 事件:咪咕笔试 知识点总结 1.Html设置格式贵阳 ...

  5. [struts2学习笔记] 第四节 学着使用struts 2的tag标签

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/40349201 官方文档: http://struts.apache.org/relea ...

  6. iOS7编程Cookbook中例15.8中一个小问题

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 该书的15.8例子标题为Editing Videos on a ...

  7. Java进阶(四十一)多线程讲解

    Java多线程讲解 前言 接到菜鸟网络的电话面试,面试官让自己谈一下自己对多线程的理解,现将其内容整理如下. 线程生命周期 Java线程具有五种基本状态 新建状态(New):当线程对象创建后,即进入了 ...

  8. ubunut系统清理系统根目录下缓存文件夹.cache超大导致磁盘不足

    在使用中突然发现系统超慢,没有做什么特别的操作. 只好重启下电脑,重启后提示系统空间不足1G.挨个查看文件夹大小,没有发现问题,然后就用Ctrl + H显示隐藏文件夹后再继续逐个查看大小,发现.cac ...

  9. Android初级教程使用服务注册广播接收者监听手机解锁屏变化

    之前第七章广播与服务理论篇写到: 特殊的广播接收者(一般发广播次数频率很高) 安卓中有一些广播接收者,必须使用代码注册,清单文件注册是无效的 屏幕锁屏和解锁 电量改变 今天在这里就回顾一下,且用代码方 ...

  10. 使用Python做简单的字符串匹配

    由于需要在半结构化的文本数据中提取一些特定格式的字段.数据辅助挖掘分析工作,以往都是使用Matlab工具进行结构化数据处理的建模,matlab擅长矩阵处理.结构化数据的计算,Python具有与matl ...