虚函数调用及实现

c++ 纯虚函数调用 错误原因 问题描述 最近写程序时,做了一个基类供其他模块调用,调试时出现了下面的错误: pure virtual method called 如图所示: 纯虚函数调用。纯虚函数是需要子类具体实现的函数,怎么就被调用了呢? 首先明确观点:永远不要在构造和析构中直接/间接调用virtual函数。 下面是根据查阅的资料和自己的理解作出的解释,供参考。 抽象类和纯虚函数 来复习一下c++中的纯虚函数和抽象... 阅读详情

【摘自】C/C++虚函数实现的基本原理

1. 概述

简单地说,每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针。例:

 

其中:

  • B的虚函数表中存放着B::foo和B::bar两个函数指针。
  • D的虚函数表中存放的既有继承自B的虚函数B::foo,又有重写(override)了基类虚函数B::bar的D::bar,还有新增的虚函数D::quz。

提示:为了描述方便,本文在探讨对象内存布局时,将忽略内存对齐对布局的影响。

2. 虚函数表构造过程

从编译器的角度来说,B的虚函数表很好构造,D的虚函数表构造过程相对复杂。下面给出了构造D的虚函数表的一种方式(仅供参考):

 

提示:该过程是由编译器完成的,因此也可以说:虚函数替换过程发生在编译时。

3. 虚函数调用过程

以下面的程序为例:

 

编译器只知道pb是B*类型的指针,并不知道它指向的具体对象类型 :pb可能指向的是B的对象,也可能指向的是D的对象。

但对于“pb->bar()”,编译时能够确定的是:此处operator->的另一个参数是B::bar(因为pb是B*类型的,编译器认为bar是B::bar),而B::bar和D::bar在各自虚函数表中的偏移位置是相等的。

无论pb指向哪种类型的对象,只要能够确定被调函数在虚函数中的偏移值,待运行时,能够确定具体类型,并能找到相应vptr了,就能找出真正应该调用的函数。

提示:本人曾在“ C/C++杂记:深入理解数据成员指针、函数成员指针 ”一文中提到:虚函数指针中的ptr部分为虚函数表中的偏移值(以字节为单位)加1。

B::bar是一个虚函数指针, 它的ptr部分内容为9,它在B的虚函数表中的偏移值为8(8+1=9)。

当程序执行到“pb->bar()”时,已经能够判断pb指向的具体类型了:

  • 如果pb指向B的对象,可以获取到B对象的vptr,加上偏移值8((char*)vptr + 8),可以找到B::bar。
  • 如果pb指向D的对象,可以获取到D对象的vptr,加上偏移值8((char*)vptr + 8) ,可以找到D::bar。
  • 如果pb指向其它类型对象...同理...

4. 多重继承

当一个类继承多个类,且多个基类都有虚函数时,子类对象中将包含多个虚函数表的指针(即多个vptr),例:

 

其中:D自身的虚函数与B基类共用了同一个虚函数表,因此也称B为D的主基类(primary base class)。

虚函数替换过程与前面描述类似,只是多了一个虚函数表,多了一次拷贝和替换的过程。

虚函数的调用过程,与前面描述基本类似,区别在于基类指针指向的位置可能不是派生类对象的起始位置,以如下面的程序为例:

 

几秒读懂C++虚函数调用的汇编代码实现 本文详细讲述C++虚函数调用的汇编代码实现 阅读详情

相关推荐

C++虚函数调用的汇编代码实现详解

在C++中,虚函数是一种特殊的成员函数,它可以在派生类中被重写,并且在运行时根据对象的实际类型来调用相应的函数。在C++中,虚函数是一种特殊的成员函数,它可以在派生类中被重写,并且在运行时根据对象的实际类型来调用相应的函数。在C++中,每个包含虚函数的类都有一个虚函数表(vtable),它是一个存储虚函数地址的数组。当对象被创建时,编译器会在对象的内存布局中插入一个指向虚函数表的指针,称为虚函数指针(vptr)。在C++中,每个包含虚函数的类都有一个虚函数表(vtable),它是一个存储虚函数地址的数组。

CodeGu的博客 92

虚函数调用的几种方式

https://www.cnblogs.com/lsgxeva/p/7692567.html  虚函数调用的几种方式  /* * 虚函数的三种调用 * 1: 指针 * 2: 引用 * 3: 对象(不能实现多态) */ #include <iostream> //继承,默认情况下class是私有继承 struct默认是公有继承 //虚函数可以调用成员函数 //多态调...

ZP_Disciple的博客 1812

手把手教你用GEM-2多频电磁仪找地下管道(附真实数据图)

本文详细解析了使用GEM-2多频电磁仪精准定位地下管线的全流程,包括设备调试、频率选择、现场扫描技巧及数据解读。通过真实案例展示了如何应对复杂干扰环境,提升管线检测效率与准确性,特别适合市政工程和地下管线普查。

weixin_42565652的博客 375

虚函数的三种调用方式——对象名、指针和引用

直接看示例代码 #include <iostream> using namespace std; class Father { public: Father(){}; virtual void func() { cout<<"Father"<<endl; } void func2() { cout<<"Father_func2"&...

SunnyZhang在奋斗的博客 9620

虚函数调用

虚函数怎么调用: (1)先找到虚函数表 (2)从虚表中找到要调用虚函数。单继承中派生类虚表的形成: 先看下述的代码:#include <iostream> using namespace std;//基类中包含虚函数和普通函数 class Base { public: virtual void FunTest1() { cout<<"Base::FunTest

xy的博客 5441

【C/C++】虚函数调用流程 | 虚函数和一般函数的区别

虚函数指向一个虚函数表,表中存放了虚函数的地址,实际的虚函数存放在代码段中。 当子类继承了父类的时候也会继承父类的虚函数表,当子类重写父类中虚函数的时候,会将继承到虚函数表中的地址替换为重写的函数地址。这个过程称为覆盖 虚函数列表中,在重写虚函数的后面是父类未重写的虚函数,然后是子类虚函数 以上的过程将在编译阶段进行调用流程不同:虚函数通过虚函数指针间接引用,普通函数可以使用函数名直接调用 调用效率不同:虚函数调用流程复杂,效率低,普通函数效率高 使用场景不同:虚函数的目的是为了实现多态 使用了虚函数

努力让自己的能力配得上梦想. 1624

调用基类的纯虚函数,如何知道纯虚函数调用哪个派生类(子类)中的实现

vptr 和 vtable: vptr 指向对象的 vtable,通过它们在运行时决定调用哪个派生类的实现。多态性: 基类指针或引用调用虚函数时,实际调用的是派生类的实现,这通过动态绑定实现。调试和分析: 使用调试器可以更深入地观察这种运行时行为。

不会编程的码农研究僧 969

虚函数的定义和实现调用

虚函数是一个在基类中声明的虚函数,它在该基类中没有定义具体的操作内容,要求各派生类根据实际需要定义自己的版本。 纯虚函数的声明格式为:   virtual 函数类型 函数名(参数表) = 0; 带有纯虚函数的类称为抽象类 注意   抽象类只能作为基类来使用。   不能定义抽象类的对象。 //通过基类的指针来调用不同的派生类对象的实现函数 #include <iostream> using namespace std; class Base1 { public: virtual vo

weixin_48657700的博客 4941

在父类的构造函数中调用虚函数可以实现多态么?

在父类的构造函数中调用虚函数是不能够实现多态的,这个问题涉及到vptr指针的初始化问题。 比如在构造子类对象c时,执行到父类的构造函数时,这个c.vptr指向的是父类的虚函数表,这时调用虚函数,执行的是父类的虚函数,不是多态。当执行完父类的构造函数,执行到子类的构造函数时,这个c.vptr指针这时才指向本身自己的虚函数表。注意,每一个对象都有一个vptr指针和一个虚函数表,这个是在编译时就...

一路向北 1533

构造函数中调用虚函数能否实现多态

问:构造函数中调用虚函数能否实现多态? #include using namespace std; class Parent { public: Parent(int a = 0) { this->a = a; print(); } virtual void print() { cout<<"Parent1"<<endl; } virtual void print2

bbs375的博客 1564

引用和指针调用虚函数实现多态

以Animal为父类,Cat为子类为例,动物类会叫,猫类会喵喵叫。未加关键字virtual之前,Animal类内部非静态成员函数,类为空,加入virtual后,Animal内部产生vfptr即虚拟函数指针,该指针大小为8字节,指向虚拟函数表vftable。文章使用引用或者指针实现调用虚函数实现多态。

mmomentx的博客 340

linux 虚函数调用性能,C++对象布局及多态实现探索之虚函数调用

我们再看看虚成员函数的调用。类C041中含有虚成员函数,它的定义如下:struct C041{C041() : c_(0x01) {}virtual void foo() { c_ = 0x02; }char c_;};执行如下代码:C041 obj;PRINT_DETAIL(C041, obj)PRINT_VTABLE_ITEM(obj, 0, 0)obj.foo();C041 * pt = &...

weixin_39726131的博客 156

构造函数中调用虚函数,能实现多态吗?

内容来源: 传智播客 c++面试中常常会问到一个问题: 通过子类对象,在父类构造函数中调用虚函数,能实现多态吗? #iinclude<iostream> using namespace std; class A { public: int a; A(int a) { this->a = a; print(); } virtual void print() ...

qq_20880939的博客 509

java基类能调用虚函数_在父类的构造函数中调用虚函数为什么不能实现多态

本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列文章。问题描述大家都知道在面向对象语言中,多态实际上就是接口的多种不同的实现方式,而虚函数实现多态的机制就是通过指向派生类的基类指针或引用,访问派生类中同名覆盖的成员函数。那么有人知道为什么在父类的构造函数中调用虚函数却不能实现多态吗?问题分析在最初接触到这个问题的时候,也很茫然,好像都知道不能在父类的构造函数里通过调用虚函数...

weixin_42515063的博客 227

MFC浅析(7) CWnd类虚函数调用时机、缺省实现

CWnd类虚函数调用时机、缺省实现<!-- body {color:black;font-family: "宋体"; font-size: 9pt} p {color:black;font-family: "宋体"; font-size: 9pt} pre {color:black;font-family: "宋体"; font-size: 9pt}

FMD STUDIO 4485

在父类的构造函数中调用虚函数为什么不能实现多态

欢迎点击「算法与编程之美」↑关注我们!本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列文章。问题描述大家都知道在面向对象语言中,多态实际上就是接口的...

算法与编程之美 790

构造和析构函数中调用虚函数不能实现多态的原因

构造期间:对象还未完全构造完毕,无法看作完整的派生类对象,因此调用虚函数只会执行当前构造类的版本;析构期间:对象的派生部分已被销毁,不能再访问子类成员,同样调用的是当前析构类的版本;因此,在构造函数和析构函数中调用虚函数不会实现多态,不推荐使用虚函数实现初始化或清理逻辑,可采用显式函数或工厂函数替代。推荐做法初始化行为可使用“工厂函数”来封装(即构造后立即调用初始化接口);析构逻辑应尽量避免依赖虚函数,可以借助基类中析构函数里调用虚函数或使用模板方法模式。

陈子青的博客 396

C++运行时通过基类指针或引用调用派生类虛函数的实现原理: 虛函数表

我们知道要实现运行时的多态, 必须在基类中声明和定义相关的虛函数, 并在派生类中重新实现基类中的虛函数. 当编译器见到这种继承层次结构的时候, 编译器将为定义了虛函数的基类和覆盖了基类虛函数的派生类分别创建一张虛函数表(Virtual Function Table, VFT), 也就是说通过编译器的编译, 基类和派生类的代码中都将有自己的虛函数表. 为这些类创建实例化对象时, 会在实例化的...

小强的机器人工坊 5209
上一篇: Golang gin反向代理
下一篇: openssh 交叉编译
阳光跑得快
博客等级 码龄14年 4粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值