目录

一、 虚函数

二、虚函数原理与虚函数表


一、 虚函数

虚函数:

使用 virtual 关键字声明的函数,是动态多态实现的基础。

非类的成员函数不能定义为虚函数。

类的静态成员函数不能定义为虚函数。

构造函数不能定义为虚函数,但可以将析构函数定义为虚函数。

当将基类的某一成员函数声明成虚函数后,派生类的同名函数自动成为虚函数。

二、虚函数原理与虚函数表

c++ 能够在运行时确定调用的函数,是因为引入了虚函数。

一旦类中引入了 虚函数,在程序编译期间(<深度探索c++对象模型 4.2>) 就会创建虚函数表,表中每一项数据都是 虚函数的入口地址。

为了将对象与虚函数表关联起来,编译器会在对象中会增加一个指针成员用于存储虚函数表的位置。

由上图可知当是一个空类的时候,系统会给类一个字节的大小,成员函数不占用类的空间,而图3用virtual关键字声明了虚函数说以类里多了一个指向虚函数表的指针,因为这里是64位的所以指针大小为8个字节;

那么现在用这个指针去访问虚函数

#include <iostream>
using namespace std;
class A
{
public:virtual void fun1(){cout << "hello world1" << endl;}virtual void fun2(){cout << "hello world2" << endl;}virtual void fun3(){cout << "hello world3" << endl;}
};
typedef void (*P)(); //重定义一下对应函数指针的类型;
int main()
{A a;cout << sizeof(a) << endl;cout << "指向虚函数表的指针的地址:" << (long *)&a << endl;//由于类里只有一个指针,说以对象的地址与指针的地址相同。cout << "虚函数表的地址:" << (long *)*(long *)&a << endl; //对指针地址取*就是指针指向的地址,也就是虚函数表的地址。cout << "第一个虚函数的入口地址:" << (long *)*(long *)*(long *)&a << endl;cout << "第二个虚函数的入口地址:" << (long *)*((long *)*(long *)&a + 1)<< endl;cout << "第二个虚函数的入口地址:" << (long *)*((long *)*(long *)&a + 2)<< endl;P p1;P p2;P p3;p1=(P)(long *)*(long*)*(long *)&a; //将对应的函数指针p指向第一个虚函数的地址;p1();//调用第一个虚函数;p2=(P)(long *)*((long*)*(long *)&a +1); //将对应的函数指针p指向第一个虚函数的地址;p2();//调用第二个虚函数;p3=(P)(long *)*((long*)*(long *)&a +2); //将对应的函数指针p指向第一个虚函数的地址;p3();//调用第三个虚函数;return 0;
}

输出结果

基类的指针或引用,指向或引用派生类对象时就是通过虚函数表的指针来找到实际应该调用的函数;

基类与派生类都维护自己的虚函数表,如果派生类重写基类的虚函数,则虚函数表存储的是派生类的函数的地址,没有重写的虚函数则保存的是基类的虚函数表;

#include <iostream>
using namespace std;
class A  //基类
{
public:virtual void fun1(){cout << "hello world1" << endl;}virtual void fun2(){cout << "hello world2" << endl;}virtual void fun3(){cout << "hello world3" << endl;}
};
class B : public A   //派生类
{
public:void fun1()  //重写基类的虚函数1{cout << "hello Linux" << endl; }
};
typedef void (*P)(); //重定义一下对应函数指针的类型;
int main()
{B b;//派生类的对象A &a=b; //基类的引用引用派生类的对象cout << sizeof(a) << endl;cout << "指向虚函数表的指针的地址:" << (long *)&a << endl;//由于类里只有一个指针,说以对象的地址与指针的地址相同。cout << "虚函数表的地址:" << (long *)*(long *)&a << endl; //对指针地址取*就是指针指向的地址,也就是虚函数表的地址。cout << "第一个虚函数的入口地址:" << (long *)*(long *)*(long *)&a << endl;cout << "第二个虚函数的入口地址:" << (long *)*((long *)*(long *)&a + 1)<< endl;cout << "第二个虚函数的入口地址:" << (long *)*((long *)*(long *)&a + 2)<< endl;P p1;P p2;P p3;p1=(P)(long *)*(long*)*(long *)&a; //将对应的函数指针p指向第一个虚函数的地址;p1();//调用第一个虚函数;p2=(P)(long *)*((long*)*(long *)&a +1); //将对应的函数指针p指向第一个虚函数的地址;p2();//调用第二个虚函数;p3=(P)(long *)*((long*)*(long *)&a +2); //将对应的函数指针p指向第一个虚函数的地址;p3();//调用第三个虚函数;return 0;
}

输出结果

虚函数原理与虚函数表相关推荐

  1. C++多态的原理(虚函数指针和虚函数表)

    C++多态的原理 (虚函数指针和虚函数表) 1.虚函数指针和虚函数表 2.继承中的虚函数表 2.1单继承中的虚函数表 2.2多继承中的虚函数表 3.多态的原理 4.总结 1.虚函数指针和虚函数表 以下 ...

  2. 虚函数的实质——虚函数表

    目录 虚函数的实质--虚函数表 虚函数表是什么? 虚函数表长什么样? 证明了确实存在隐藏的虚函数表指针 派生类指针转换为基类类型的实质 当基类指针指向派生类对象时,虚函数表会如何变化? 虚函数的实质- ...

  3. 虚函数和纯虚函数及虚函数表

    虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 虚函数 引入 ...

  4. 虚函数与纯虚函数以及虚函数表之间的关系

    1.虚函数 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.C++中虚函数的作用主要是实现多态机制.所谓多态就是用父类指针指向子类对象,然后通过父类指针调用实际子类的成员函数,这种技术 ...

  5. C++中虚继承产生的虚基类指针和虚基类表,虚函数产生的虚函数指针和虚函数表

    本博客主要通过查看类的内容的变化,深入探讨有关虚指针和虚表的问题. 一.虚继承产生的虚基类表指针和虚基类表 如下代码:写一个棱形继承,父类Base,子类Son1和Son2虚继承Base,又来一个类Gr ...

  6. c/c++入门教程 - 2.4.7 多态、函数地址晚绑定(重写,虚函数,纯虚函数,抽象类,虚析构,纯虚析构)

    目录 4.7 多态 4.7.1 多态的基本概念(超级重要) 4.7.2 多态的原理刨析(超级重要) 4.7.2 多态案例一:计算器类 4.7.3 纯虚函数和抽象类 4.7.4 多态案例二 - 制作饮品 ...

  7. C/C++编程:虚函数与纯虚函数

    虚函数 VS 纯虚函数 虚函数 虚函数是应在派生类中重新定义的函数.当使用指针或者对基类的引用来引用派生类的对象时,可以为该对象调用虚函数并执行该派生类的版本. 虚函数的"虚",虚 ...

  8. c++中的虚特性(虚基类、虚函数、纯虚函数)

    1. 虚基类 1.1 虚基类作用 为了解决多继承时的命名冲突和冗余数据问题,使得派生类中只保留一份间接基类的成员. 其本质是是让某个类做出声明,承诺愿意共享它的基类.其中,这个被共享的基类就称为虚基类 ...

  9. 【C++】虚函数指针和虚函数列表

    本篇文章主要来讲述,C++多态的实现原理,也就是虚函数和虚函数列表是怎么回事?它们是如何实现多态的? 虚函数概述: 首先,C++多态的实现是通过关键字virtual,有了这个关键字之后,通过继承的关系 ...

最新文章

  1. java保留两位小数_java使double保留两位小数的多方法 java保留两位小数
  2. WPF error: does not contain a static 'Main' method suitable for an entry point
  3. POJ 1061 青蛙的约会(扩展欧几里得)
  4. asp.net mysql打包_Asp.net与SQL一起打包部署安装
  5. 朱海舟宣布新一批应用已经适配锤子TNT 网友:救救海舟
  6. Response.Write 用法总结
  7. 从氨基酸到大分子(蛋白质、核酸)
  8. 多核技术对计算机技术的影响,计算机新技术——多核技术.doc
  9. 魔兽世界服务器卡顿原理,《魔兽世界》怀旧服卡顿解决方法
  10. C++中i++和++i的区别
  11. Python流程控制
  12. 计算机管理储存u盘无法使用,U盘无法识别的三种常见情况
  13. 【SystemVerilog基础】关于随机化约束solve...before的深入探究
  14. 多人交互与体感游戏开发相关技术说明
  15. arcgis多个图共用一个图例_ArcGIS制图技巧,一个小技巧使图例与之匹配!
  16. 霍夫丁不等式(Hoeffding‘s inequality)-集成学习拓展-西瓜书式8.3
  17. ICMP协议是什么协议?
  18. 1.《小狗钱钱》读书笔记
  19. mysql 乱码 越南_mysql数据库乱码之保存越南文乱码解决方法
  20. 【jQuery】利用lazyload.js延迟加载技术应对多图杀猫的单一页面

热门文章

  1. 深度学习:维度灾难(Curse Of Dimensionality)
  2. android 经纬度工具类,工具类之LocationUtils(定位工具类)
  3. Uedit32设置字体大小
  4. 数字签名与数字证书技术简介(二)
  5. 数据库基本知识和命令
  6. 用wvdial和ppp轻松上网
  7. 国密SM4算法(简介与C源码)
  8. 【密码学|算法设计】拓展的欧几里得算法及理论证明 (Extended Euclidean Algorithm)
  9. js中for(i in array)和for(i=0;i<array.length;i++)之间的坑
  10. VM 将宿主机文件夹 映射至 虚拟机以及vm tools【共享文件夹、复制粘贴、拖动上传下载】