所谓虚函数,虚就虚在“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被称为“虚”函数。

而什么是动态联编呢?

编译程序在编译阶段并不能确切地知道将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切地知道将要调用的函数,要求联编工作在程序运行时进行,这种在程序运行时进行的联编工作被称为动态联编,或动态束定,又叫晚期联编;C++规定:动态联编是在虚函数的支持下实现的;

虚函数是动态联编的基础;虚函数是成员函数,而且是非静态的成员函数;虚函数在派生类中可能有不同的实现,当使用这个成员函数操作指针或引用所标识的对象时,对该成员函数的调用采用动态联编方式,即:在程序运行时进行关联或束定调用关系;
          动态联编只能通过指针或引用标识对象来操作虚函数;如果采用一般的标识对象来操作虚函数,将采用静态联编的方式调用虚函数;

如果一个类具有虚函数,那么编译器就会为这个类的对象定义一个指针成员,并让这个指针成员指向一个表格,这个表格里面存放的是类的虚函数的入口地址;比如:一个基类里面有一些虚函数,那么这个基类就拥有这样一个表,它里面存放了自己的虚函数的入口地址,其派生类继承了这个虚函数表,如果在派生类中重写/覆盖/修改了基类中的虚函数,那么编译器就会把虚函数表中的函数入口地址修改成派生类中的对应虚函数的入口地址;这就为类的多态性的实现提供了基础;

多态是什么?

               在程序设计领域,一个广泛认可的定义是“一种将不同的特殊行为和单个泛化记号相关联的能力”。和纯粹的面向对象程序设计语言不同,C++中的多态有着更广泛的含义。除了常见的通过类继承和虚函数机制生效于运行期的动态多态(dynamic polymorphism)外,模板也允许将不同的特殊行为和单个泛化记号相关联,由于这种关联处理于编译期而非运行期,因此被称为静态多态(static polymorphism)

说了这么多虚函数,我们要知道虚函数是面向对象程序设计的关键部分,虚函数需要借助指针和引用来实现多态,   而对象的多态性需要通过虚表和虚表指针来完成,虚表指针被定义在对象首地址的前4个字节处。因此虚函数必须作为成员函数使用。(访问虚函数需要this指针。)

当我们在类中定义了虚函数后,他会包含一个隐藏的数据成员(虚表指针),看代码:

#include<iostream>
using namespace std; class CV1{
int a;
}; class CV2{
virtual void a(){}
virtual void b(){}
int c;
}; int main()
{
int nsize1 = sizeof(CV1);
int nsize2 = sizeof(CV2);
return 0;
}

看一下反汇编代码:

ok提到了虚函数表,下面我们来看一下虚函数表:

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

而虚表指针是如何初始化的呢 ?  他是通过编译器在构造函数内插入代码来完成的,用户没有编写构造函数的时候,由于必须初始化虚表指针,因此编译器会提供默认构造函数,以完成虚表指针的初始化。

由于虚表信息在编译后会被链接到对应的可执行文件中,因此所获得的虚表地址是一个相对固定的地址,虚表中虚函数的地址的排列顺序依据虚函数在类中的声明顺序而定,先声明的虚函数的地址会被排列在虚表中靠前的位置。

对于含有构造函数的类而言虚表初始化过程和默认构造函数是相同的,都是以对象首地址的前4字节数据保存虚表的首地址。

如图:

在虚表初始化过程中,对象执行构造函数后,得到虚表指针,当其他代码访问这个对象的虚函数的时候,会根据对象的首地址取出对应虚表元素。当函数被调用时,会间接访问虚表,得到对应的虚函数首地址 并调用执行。

对于虚表指针的初始化,其代码部分被编译器隐藏掉了,当类中出现虚函数时,必须在构造函数中对虚表指针执行初始化操作,没有虚函数的类对象在构造时不会进行初始化虚表的操作。

对于单继承的类结构,在某个成员函数中,将this指针的地址初始化为虚表地址时,可以判定这个成员函数就是构造函数。

某些特征:

1.类中隐式定义了一个数据成员;

2.该数据成员在首地址处,占4字节;

3.构造函数会将此数据成员初始化为某个数组的首地址;

4.这个地址属于数据区,是相对固定的地址;

5.在这个数组内,每个元素都是函数指针;

6.仔细观察这些函数,他们被调用时,第一个参数必然是this指针;(主意调用约定)

7.在这些函数内部,很有可能会对this指针使用相对间接的访问方式。

总的来说,类中所有的虚函数都在虚表当中,而虚表的查找又需要得到指向它的虚表指针,虚表指针又是在构造函数中被初始化为虚表首地址, 因此,要想找到虚函数就得得到虚表的首地址。

jofranks  13.7.26 于南昌

[置顶] 【C/C++学习】之十三、虚函数剖析的更多相关文章

  1. C++学习25 纯虚函数和抽象类

    在C++中,可以将成员函数声明为纯虚函数,语法格式为: ; 纯虚函数没有函数体,只有函数声明,在虚函数声明结尾加上=0,表明此函数为纯虚函数. 最后的=0并不表示函数返回值为0,它只起形式上的作用,告 ...

  2. 学习笔记---C++虚函数,纯虚函数

    1 .虚函数 假设people是man的父类,people类和man类都定义了实函数walk() people* p = new man(); p->walk(); 这里P执行的是people类 ...

  3. 深入学习c++(虚函数遇到析构函数就退化了)

    1. 在构造函数和析构函数中调用的虚函数并不具备虚函数的特性 因为基类的构造函数先构造, 析构函数后析构

  4. C++学习笔记--从虚函数说开去

    虚函数与纯虚函数: 虚函数:在某基类中声明为virtual并在一个或多个派生类中被重新定义的成员函数,virtual  函数返回类型  函数名(参数表){函数体;} ,实现多态性,通过指向派生类的基类 ...

  5. c++学习之多态(虚函数和纯虚函数)

    c++是面向对象语言,面向对象有个重要特点,就是继承和多态.继承之前学过了,就是一种重用类的设计方式.原有的类叫父类,或者基类,继承父类的类叫子类.在设计模式中,我们总是要避免继承,推荐用组合.因为继 ...

  6. C++学习笔记27,虚函数作品

    C++它指定虚函数的行为,但实现的作者编译器. 通常,编译器处理虚函数的方法是给每个对象加入一个隐藏成员.隐藏成员中保存了一个指向函数地址数组的指针. 这个数组称为虚函数表(virtual funct ...

  7. [置顶] Deep Learning 学习笔记

    一.文章来由 好久没写原创博客了,一直处于学习新知识的阶段.来新加坡也有一个星期,搞定签证.入学等杂事之后,今天上午与导师确定了接下来的研究任务,我平时基本也是把博客当作联机版的云笔记~~如果有写的不 ...

  8. [置顶] Firefox OS 学习——简单了解知识

    什么是Firefox OS ? Firefox OS 是一个为网页设计而生的能编译和独立的手机网页操作系统,我们相信在接下来的时代,网页应用将充满整个新兴操作设备,这也为当前许多网页开发者不需要太多的 ...

  9. [置顶] Ajax 初步学习总结

    Ajax是什么 Ajax是(Asynchronous JavaScript And XML)是异步的JavaScript和xml.也就是异步请求更新技术.Ajax是一种对现有技术的一种新的应用,不是一 ...

随机推荐

  1. apache将请求转发到到tomcat应用

    映射: 1.开启apache中的proxy模块(proxy.conf,proxy.load,proxy_http.load) 2.配置apache配置文件,<VirtualHost *:80&g ...

  2. php 序列化储存和转化 json_encode() json_decode($q,true)

    序列化是将变量转换为可保存或传输的字符串的过程:反序列化就是在适当的时候把这个字符串再转化成原来的变量使用.这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性. 例如:当需要数据库只有一个 ...

  3. Python核心编程(第九章)--文件和输入输出

    文件内建函数: open()函数提供了初始化输入/输出操作的通用接口 open()基本语法:file_object = open(filename,access_mode='r',buffering= ...

  4. 在Ubuntu上安装VmTools

    1.添加VmTools 2.解压 .tag.gz文件 使用Linux命令: tar –zxvf src –c dis -c: 建立压缩档案 -x:解压 -t:查看内容 -r:向压缩归档文件末尾追加文件 ...

  5. nodejs+express Mvc站点

    nodejs+express Mvc站点 像asp.net Mvc一样开发nodejs+express Mvc站点 首先,我是个c#码农.从事Mvc开发已然4个年头了,这两年前端MVC的兴起,我也跟风 ...

  6. wordpress显示多个分类的文章

    显示多个分类下的文章可以这样提取: <?php query_posts(array('posts_per_page'=>10,'orderby'=>'rand','category_ ...

  7. UIAutomator 学习版

    1.要写UIAutomator的testcase,首先要用Eclipse创建一个Java Project 需要将Junit 的lib加入到工程里 2.添加android.jar和uiautomator ...

  8. Flash型单片机的加密与解密

    厂商利用单片机进行产品开发时,都会关心其代码和数据的保密性.考虑到用户在编写和调试代码时所付出的时间和精力,代码的成本是不言而喻的.    早期的单片机,代码是交给芯片制造商制成掩膜ROM.有两种加密 ...

  9. android:duplicateParentState属性解释

    android:duplicateParentState指的是当前控件是否跟随父控件的(点击.焦点等)状态 例:假设一Layout有两子View,对Layout进行监听点击事件:子ViewA一个设置d ...

  10. Magento How To Display Product Custom Option On list.phtml

    Some time we need to display custom option of product on category list page to achive this task we o ...