当一个类中有一个或多个虚函数时,内存中会多一个虚指针(vptr,virtual pointer),指向一个虚表(vtbl,virtual table)

父类有虚函数,则子类一定有虚函数

在下图示意图中,我们可知,B继承A,C继承B。
A有两个虚函数,B继承A则也有两个虚函数,同时B对虚函数vfunc1进行了重写,同理,C继承B,则C也有两个虚函数,C对虚函数vfunc1也进行了重写。故在内存中,A B C对应的虚函数vfunc2是同一个均为A::vfunc2,对应的虚函数vfunc1分别为A::vfunc1,B::vfunc1,C::vfunc1。


c++编译器遇到函数调用时,会有两个考量,是静态绑定还是动态绑定,静态绑定是call xxx地址,即一定调用到某个地址,动态绑定可以调用到不同的地址

动态绑定需符合三个条件:

  • 通过指针调用(对象调用函数是静态绑定
  • 指针是向上的关系,向上关系是指,比如 父类 名称 = new 子类(),这样的关系即是一种向上的关系
  • 调用的是虚函数

虚函数的这种用法,称之为多态

示例程序:

#include<iostream>
using namespace std;class A {public:A(int data1, int data2) :m_data1(data1), m_data2(data2) {}virtual void vfunc1() { cout << "调用的是A的vfunc1"<< endl; }virtual void vfunc2() { cout << "调用的是A的vfunc2" << endl; }void func1() { cout << "调用的是A的func1" << endl; }void func2() { cout << "调用的是A的func2" << endl; }void getData() {cout << "A的m_data1:" << m_data1 << ",A的m_data2:" << m_data2 << endl;}
private:int m_data1, m_data2;
};class B:public A {public:B(int n1,int n2,int n3) :A(n1,n2), m_data3(n3){}virtual void vfunc1() { cout << "调用的是B的vfunc1" << endl; }void func2() { cout << "调用的是B的func2" << endl; }void getData() {A::getData();cout << "B的m_data3:" << m_data3  << endl;}
private:int m_data3;
};class C :public B {public:C(int n1, int n2, int n3,int n4,int n5):B(n1,n2,n3) , m_data1(n4), m_data4(n5) {}virtual void vfunc1() { cout << "调用的是C的vfunc1" << endl; }void func2() { cout << "调用的是C的func2" << endl; }void getData() {B::getData();cout << "C的m_data1:" << m_data1 << ",C的m_data4:" << m_data4 << endl;}
private:int m_data1, m_data4;
};

测试程序:

int main()
{A a(1,2);a.vfunc1();a.vfunc2();a.func1();a.func2();a.getData();cout << endl;B b(3,4,5);b.vfunc1();b.vfunc2();b.func1();b.func2();b.getData();cout << endl;C c(6,7,8,9,10);c.vfunc1();c.vfunc2();c.func1();c.func2();c.getData();cout << endl;A* d = new B(1,3,5);d->vfunc1();d->vfunc2();d->func1();d->func2();d->getData();cout << endl;A* d2 = new C(1, 3, 5,7,9);d2->vfunc1();d2->vfunc2();d2->func1();d2->func2();d2->getData();system("pause");return 0;
}

输出结果:

调用的是A的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是A的func2
A的m_data1:1,A的m_data2:2调用的是B的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是B的func2
A的m_data1:3,A的m_data2:4
B的m_data3:5调用的是C的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是C的func2
A的m_data1:6,A的m_data2:7
B的m_data3:8
C的m_data1:9,C的m_data4:10调用的是B的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是A的func2
A的m_data1:1,A的m_data2:3调用的是C的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是A的func2
A的m_data1:1,A的m_data2:3

从输出结果中,我们可以看出,当对函数进行重写后,子类在调用函数时会调用重写后的函数。当函数是虚函数时,若我们按照父类 名称 = new 子类() 的形式生成指针,则该指针在调用函数时,若调用的函数是虚函数,则其会调用子类重写后的虚函数,若调用的函数不是虚函数,则无论子类对该函数是否重写,调用的均为父类的函数。

c++面向对象高级编程 学习十六 vptr和vtbl相关推荐

  1. c++面向对象高级编程 学习十五 组合继承关系下的构造和析构

    文章目录 继承关系 组合关系 继承和组合 继承关系 构造由内而外,析构由外而内,内即是父类 组合关系 A拥有B, 构造由内而外,析构由外而内,内即是B 继承和组合 构造和析构顺序如图:

  2. c++面向对象高级编程 学习十四 引用

    文章目录 reference reference的常见用途 reference 变量有三种形式:值,指针,引用 int x=0; //值 int* p=&x;//指向整型的指针,地址,指针在之 ...

  3. c++面向对象高级编程 学习十二 模板

    模板特化: 模板是一种泛化的形式,特化是将参数类型进行指定,写出特化的版本,当在调用下图cout<<hash()(1000);的时候,由于特化中有struct hash{ }的版本,因此会 ...

  4. c++面向对象高级编程 学习十 function-like classes

    本节是设计一个class,使它的行为像一个函数. 如果一个东西能接受小括号()操作符,那么这个东西就称之为函数,或像函数的东西. 下图为三个函数对()的重载,这三个类均为像函数的类,它们可接受()操作 ...

  5. c++面向对象高级编程 学习六 虚函数

    虚函数:在成员函数前面加上virtual,函数就变成了虚函数 继承函数:子类可以调用父类的函数,叫做继承了函数,即函数的调用权 三种函数: non-virtual 函数: 你不希望 derived c ...

  6. c++面向对象高级编程 学习五 组合、委托与继承

    组合 composition 表示has a queue类中有一个deque容器,这种关系叫做 组合 queue中的六个函数都是调用c的函数完成的 template <class T> c ...

  7. c++面向对象高级编程 学习十七 const, new, delete

    文章目录 常量成员函数 new和delete 常量成员函数 常量成员函数是不改变成员数据. 当成员函数的const和non-const版本同时存在时,const object只能调用const版本,n ...

  8. c++面向对象高级编程 学习十三 数量不定的模板参数,auto,for

    文章目录 数量不定的模板参数 auto ranged-base for 数量不定的模板参数 void print() {} //... 表示参数的数量不定 template<typename T ...

  9. c++面向对象高级编程 学习十一 类模板、函数模板、成员模板

    namespace经验谈: 团队中函数或类的名字可能会冲突,因此使用namespace进行区分. 类模板: template<typename T> 函数模板: template<c ...

最新文章

  1. Java学习总结:18
  2. Gulp快速入门教程
  3. 计算机c1 c语言答题,全国计算机级考试二级C语言上机答题技巧.doc
  4. mysql group by 天_MySQL group by语句如何优化
  5. 快速上手SpyGlass——基本流程
  6. NGINX内容缓存配置
  7. Go实现Raft第四篇:持久化和调优
  8. ElasticSearch核心基础之映射
  9. hbase查看表结构_HBase基本使用之DDL学习
  10. bzoj 1045: [HAOI2008]糖果传递
  11. 论发SCI论文和生孩子的共同点:那我这篇怀的也太久了!
  12. [经验教程]iPhone苹果手机iOS系统App Store怎么下载手机APP到苹果iPhone手机?
  13. 人口模型(Malthus+Logistic)
  14. 毫米和像素怎么换算_像素换算(像素和毫米换算器)
  15. 图片转文字,实用的图片文字识别工具
  16. 魔兽世界插件开发:Beginning Lua with World of Warcraft Add-ons 中文翻译及学习 (1.1)
  17. 虚拟麦克风音频输入_硅麦克风电路连接指南
  18. 设备管理 android问号,设备管理器里有问号怎么办
  19. javabean/Listjavabean与map/Listmap互相转换
  20. java ebcdic编码转换_EBCDIC编码转换为ASCII编码

热门文章

  1. ArcGIS Engine Runtime 10 Setup步骤
  2. 【转】VScode tasks.json和launch.json的设置
  3. 由浅到深理解ROS(5.1)- roslaunch 学习
  4. jar 退出run_Java终止线程的三种方式
  5. 【Python CheckiO 题解】Between Markers (simplified)
  6. 广播延时大约多久_在长沙广播电台打广告要多少钱?
  7. *【CodeForces - 122D】Lucky Transformation(字符串问题,思维剪枝,优化,有坑,需注意的问题if的层次总结)
  8. 【51nod - 前缀异或】 对前缀和的理解
  9. 【九度oj 1135】【OpenJ_Bailian - 2915】 字符串排序 (水)
  10. react div组件设置可点击不可点击_React面试全解