关于C++虚函数与普通函数的编译与调用机制

咱们一起学C++ 第二百一十五篇之C++中虚函数的选择与早捆绑、晚捆绑的奥秘 大家好!在C++的学习过程中,我们不断接触到各种有趣又复杂的概念,今天咱们来深入探讨一下虚函数的选择以及与之紧密相关的早捆绑和晚捆绑机制。我希望通过分享这些知识,能和大家一起进步,让我们在C++编程的道路上走得更稳更远! 阅读详情

本文转自:http://www.cnblogs.com/huhuuu/p/3463109.html

----------------------------------------------------------------------------------------

引出:写个类A,声明类A指针指向NULL,调用类A的方法会有什么后果,编译通过吗,运行会通过吗?

(在VS2008与VC++的情况下) 有错误欢迎批评指正!

复制代码
#include<stdio.h>
#include<iostream>
using namespace std;

class base{
    int a;
public:
    void fun(){
        printf("base fun\n");
    }
};


int main(){
    base *b=NULL;
    b->fun();
}
复制代码

看到这个的时候,一定以为运行会报错吧。

但是奇迹般的,编译器输出了:base fun

复制代码
#include<stdio.h>
#include<iostream>
using namespace std;

class base{
    int a;
public:
    virtual void fun(){
        printf("base fun\n");
    }
};


int main(){
    base *b=NULL;
    b->fun();
}
复制代码

在看这个代码,还以为会输出base fun么,又错了,运行报错!

为什么会是这个结果?

复制代码
#include<stdio.h>
#include<iostream>
using namespace std;

class base{
    int a;
public:
    virtual void fun(){
        printf("base fun\n");
    }
        void fun2(){
        printf("base fun\n");
    }
};


int main(){
    base *b=NULL;
    b->fun();
    b->fun2();
}
复制代码

可以发现,一个是虚函数,一个普通函数

在观察下内存中得情况:

发现果然虚函数还没在内存中,而fun2已经在内存中了

在看看汇编:

明显发现虚函数的调用比普通函数多了好几个步骤,

ecx 中放的this 指针,所以this=0(NULL),但是普通函数fun2放在全局内存区,所以可以访问

而虚函数是根据虚函数表寻找的,这时没有虚函数表,自然就没法查到虚函数的地址了

 

感谢 hoodlum1980 更详细的说明:因为非虚函数的地址对编译期来说“静态”的,也就是函数地址在编译期就已经确定了,实例地址对于非虚函数只是那个 this 指针参数。所以只要不访问类的实例数据就没什么问题。而虚函数的地址,是先到实例的地址前面去查找它的虚函数表所在的地址。然后从虚函数表里取出该函数所对应的元素(虚函数表是一个函数指针数组)来call的。(当然一个已知的类的虚函数表的内容也是编译期静态的,但不同类的虚函数表内容不同,即运行时多态的基础)所以实例如果为NULL,是个有特殊意义的值,是会触发运行时错误的。

 

 

总结:类中的虚函数是动态生成的,由虚函数表的指向进行访问,不为类的对象分配内存,就没有虚函数表就无法访问。

   类中的普通函数静态生成,不为类的对象分配内存也可访问。

深入了解C++对象模型:探索内存布局及成员函数调用机制 事实上,编译器通常会在对象的内存中增加一些额外的成员变量,用于管理对象的信息。输出结果为8,因为我们增加了一个虚函数,所以编译器在对象的内存中增加了一个指向虚函数表(vtbl)的指针。这个例子中,我们定义了一个名为foo的全局函数,它与A类的成员函数foo具有相同的功能。输出结果为8,因为编译器在B对象的内存中增加了一个指向A子对象的偏移量(offset)的指针。C++成员函数通常被翻译成带有一个额外的隐含参数this的普通函数,this指向调用该成员函数的对象。输出结果为4,即A对象占用4个字节大小。 阅读详情

相关推荐

ExpressLRS 915MHz vs 2.4GHz:实测对比与TX12遥控器的最佳搭配方案

本文深入对比了ExpressLRS 915MHz与2.4GHz两种频段在穿透能力、延迟、抗干扰等方面的实测表现,并结合RadioMaster TX12遥控器的硬件特性,提供了从微型花飞到极限远航等不同场景下的最佳搭配方案与EdgeTX系统配置指南,帮助FPV玩家根据核心飞行需求做出精准选择。

milk8的博客 446

C++笔记-虚函数

对虚函数的调用可能在运行时才被解析。当某个虚函数通过指针或引用调用时,编译器产生的代码直到运行时才能确定该用哪个版本的函数。 另一方面,对于非虚函数的调用在编译时进行绑定。通过对象进行的函数(虚函数或非虚函数)调用也在编译时绑定。 如果虚函数使用默认实参,则基类和派生类中定义的默认实参最好一致。 在某些情况下,我们希望执行某个特定版本的虚函数,需使用作用域运算符明确指出。 含有(或者未经覆盖直接...

I'm here 423

lattice Diamond Programmer程序下载

Lattice Diamond Programmer(支持Lattice FPGA与CPLD程序下载)

qq_44385725的博客 5183

什么是在编译期确定什么是在运行期确定

一、编译期与运行期 1)常数值在编译时就确定,变量值要到运行时才确定;2)局部变量相对于栈基址的偏移,编译时就确定,堆空间变量的相对位置也要运行时才确定;3)静态变量在用户空间的地址【虚拟地址】编译时就确定,而非静态变量运行时才确定;4)非虚函数的执行编译时就确定,而虚函数的执行运行时才确定;5)直接调用虚函数时,虚函数在虚表中的偏移(或索引)编译时就确定,而通过函数指针调用虚函数时,这个索引值...

weixin_34129145的博客 988

【重磅干货】介质损耗因数(tanδ)揭秘:如何精准定位材料劣化最严重的温度?附权威研究PDF!

是评估绝缘材料性能的核心指标,能直接反映材料内部能量损耗和微观结构变化。通过分析tanδ-温度曲线的峰值点,可精准定位材料劣化最严重的温度。:极化损耗峰值的温度点(如环氧树脂的80℃)是材料性能拐点,需重点关注!:图3对比了5种材料的tanδ-温度曲线,峰值温度差异高达50℃!在电力设备、电子元器件等领域,材料的老化与温度密切相关。:[《介质损耗峰值温度在设备寿命预测中的应用》]在不同温度下测量tanδ,绘制曲线图。:[《多材料介质损耗峰值温度对比研究》]:基于tanδ峰值温度开发的。

mubiao05的博客 1351

Simulink玩转PMSM无感FOC:从IF强拖参数调试到切换失败的避坑指南

本文深入探讨了Simulink在PMSM无感FOC控制中的IF强拖启动参数调试与切换避坑技巧。通过详细解析强拖加速、速度维持和闭环切换三大阶段的设计逻辑,以及五个常见陷阱的解决方案,帮助工程师快速定位启动失败和切换震荡问题。文章还提供了典型故障的波形诊断方法和高级调试工具链优化建议,是电机控制领域的实用指南。

weixin_29164843的博客 408

虚函数使用时对应的编译原理

类的普通函数(非虚函数),在内存中地址是唯一的,可以把他们想象成普通函数,只不过第一个参数是this指针,在通过类对象指针调用时,编译器会根据类型找到相应的非虚函数的地址,这个工作是编译期完成的 每个类都会维护一个虚表,编译时,编译器根据类的声明创建出虚表,对象被构造时,虚表的地址就会被写入到这个对象内存的起始位置。父类、子类的虚表是不同的,虚表里面的内容也是不一样的(虚表里面记录了要执行的函数的函数指针)。虚函数在执行是一个动态的过程,并不是在编译时就确定下来要执行哪一个函数,而是运行时从虚表查到真正要执

wzm_c1386666的博客 245

海康门禁二次开发实战:用Java实现员工考勤系统的人脸权限管理

本文详细介绍了如何利用Java技术栈进行海康威视门禁系统的二次开发,实现员工考勤系统的人脸权限管理。从开发环境准备、SDK集成到设备连接、人脸数据批量处理,再到权限管理与异常处理,全面解析了海康门禁对接开发的关键步骤和最佳实践,助力企业构建高效、安全的智能考勤系统。

weixin_29191669的博客 262

在C++中,要实现动态联编,必须使用(基类指针)调用虚函

LAKE521的专栏 1万+

USB摄像头开发避坑指南:UVC协议中的Alternate Setting详解

本文深入解析UVC协议中的Alternate Setting机制,这是USB摄像头开发中影响视频流稳定性的关键。文章通过实际案例,详解了Alternate Setting如何管理USB带宽分配,并分析了帧率不达标、图像花屏等典型问题的根源与排查方法,为开发者提供实用的避坑指南和调试技巧。

meat5的博客 240

从编解码芯片看Tesla T4的隐藏实力:双NVDEC如何实现70路1080P解码?

本文深入剖析了英伟达Tesla T4显卡的隐藏实力,重点解读其独特的双NVDEC硬件解码器设计如何突破性能瓶颈。通过分析Turing架构、对比HEVC/H.264解码差异,并结合驱动与SDK优化,揭示了实现高达70路1080P视频流并发解码的原理与实战配置,为云游戏、直播转码等高并发场景提供高效解决方案。

weixin_29209801的博客 486

C++的新特性

介绍       也许你已经意识到了,在ISO 标准中C++语言已经被更新了。对于新的C++ 语言的编码名字已经改为C++0x, 许多编译器都已经介绍了它的一些特性。这个指南将尝试给你介绍C++ 语言的新特性。请注意,尽管这些特性已经应用到其他的编译器上,但我只在 Visual C++ 2010 编译器上解释一些新的特性。在其他编译器上绝对的语法规则我可不敢解说。       这篇文章假设你有

鹏哥说编程 4656

Linux内核配置选项详解及编译过程

在内核编译完成后,可以通过重启系统并选择新编译的内核来使用它。可以在该菜单下选择需要编译进内核的设备驱动,也可以选择将设备驱动编译为模块。可以在该菜单下选择需要编译进内核的设备驱动,也可以选择将设备驱动编译为模块。可以选择需要的调试选项,并根据需要启用或禁用相应的功能。可以选择需要的文件系统类型,并根据需要启用或禁用相应的选项。可以选择需要的网络协议和功能,并根据需要启用或禁用相应的选项。可以选择需要的调试选项,并根据需要启用或禁用相应的功能。可以选择需要的文件系统类型,并根据需要启用或禁用相应的选项。

TechGlide的博客 747

通过一个实例来分析C++中虚函数的调用原理

虚函数是C++中一个重要的概念,搞清楚这个概念对于理解C++运行的内在机制有一定的帮助。下面通过一个例子来总结一下C++中虚函数的调用原理。 示例代码: [code="C++"] #include #include using namespace std; class A { public: void set(int i,int j) ...

iteye_16074的博客 346

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Hello C++(十四)——C++类成员函数调用分析

一、C++类成员函数 1、C++类成员函数的编译 C++中的函数在编译时会根据命名空间、类、参数签名等信息进行重新命名,形成新的函数名。函数重命名的过程通过一个特殊的Name Mangling(名字编码)算法来实现。Name Mangling算法是一种可逆的算法,既可以通过现有函数名计算出新函数名,也可以通过新函数名逆向推导出原有函数名。 Name Mangling算法可以确保新函数名的唯一性,只要命名空间、所属的类、参数签名等有一个不同,那么产生的新函数名也不同。 不同的编译器有不同的Name M.

天山老妖 1万+

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

c++的this指针及对象加载的原理

指针为NULL可以调用成员函数 成员函数调用机制 成员函数在被调用时候总会传入一个 this 指针 class A { public: void show( ){ cout<<"Im is A"<<endl; } }; int mian(int argv , char[] args) { A a; a.show(); } 输出:Im is A 当你这...

老舍 258

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

哪些函数不能成为虚函数

常见的不能声明为虚函数的有:普通函数(非成员函数);静态成员函数;内联成员函数;构造函数;友元函数。 1.为什么C++不支持普通函数为虚函数? 普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。 2.为什么C++不支持构造函数为虚函数? 2.1含有虚函数的类包含一个虚指针,虚指针存储在对象的内存空间的。如果构造函数是虚的,就需要通过虚指针来调用虚函数表虚函数,可是对象还没有实例化,也就是内存空间还没有,就没有虚指针,所以构造函数不

huangdenan的博客 1572

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

纯虚函数 vs 普通虚函数:90%程序员忽略的性能与设计差异

深入解析C++纯虚函数的实现机制,对比普通虚函数在性能与设计上的关键差异。涵盖抽象类设计、动态绑定开销及接口定义最佳实践,提升程序扩展性与运行效率。值得收藏

FastDebug的博客 369

C++各类函数调用实现分析

C++函数调用一般分为三类:1.不同函数调用。2.类方法调用。3.类虚函数方法调用。这三类方法调用是如何运作的呢,其中的玄机到底是什么,今天写了一个简单程序,并通过objdump得到汇编代码进行分析。代码实例如下:void FunctionNormal( int value ){ int b = value;}class VirtualBase{ public:  virtual void test1(int value) = 0;  virtual void test2(int value) = 0;};

sxf_824的博客 1251

《深度探索C++对象模型》第四章 Function语意学收货

目录 1.编译器对类普通成员函数的处理 2.编译器对静态成员函数的处理 3.编译器对虚函数的处理 4.虚函数指针和虚函数表 5.虚函数继承中的几种情况区分 情况一: 情况二: 情况三: 情况四: 6.多重继承下的虚函数表布局 ???但是其中对于打星号的调整this指针的项不是很理解。 7.inline关键词理解 1.编译器对类普通成员函数的处理 我们知道,其实类是编译...

qq_34489443的博客 349

C++幕后故事(六)--函数我来调你了

C++幕后故事(六)–函数我来调你了 这个章节我们会学到以下3个知识点: 1.不同的类型函数是怎么调用的。 2.成员函数指针各个模型实现的原理是什么以及各个指针的效率如何。 3.inline函数的注意事项。 1.普通成员调用 看下面的代码,这里面我分别调用了类成员和全局函数 class NormalCall { public: void Add(int number) { ...

Mingyueruya的专栏 679

【C++性能优化关键一步】:彻底搞懂成员函数指针的this绑定机制

掌握成员函数指针的 this 绑定机制,解决C++性能优化难题。深入解析其在回调函数、委托模式中的应用,揭示隐式this传递原理与调用开销优化方法。理解底层实现,提升代码效率,值得收藏。

FastCompile的博客 646

c++指针访问小结

c++指针在类族中如何访问类成员。 问题描述:指针进行转换时,指针如何访问类普通成员和虚函数成员。 class A; class B: class A; B *pb; A *pa = (A*)pb; pa->f(); 指针访问首先根据指针的声明类型确定访问的范围,比

bblueske的专栏 448

C++对象布局及多态实现的探索(三)

  普通成员函数的调用  从这部分开始我们除了利用内存的信息打印来进行探索外,更多的会通过跟踪和观察编译器产生的汇编代码来理解编译器对这些语言特性的实现方式。汇编方面知识的讨论超出了本文的范围,我只对和我们讨论相关的汇编代码进行解析。理解本文要讨论的知识并不需要有很完整的汇编知识,但必须了解起码的概念。  下面我们看看引入虚继承后的影响。为了有所对比我们首先看看普通成员函数的调用情况。  执行如下

技术资料库 1142

C++入门13——详解多态2

详解虚函数表以及多态的原理

weixin_73629886的博客 757
上一篇: 最长回文串
下一篇: linux与windows下多线程work模式实现
typ2004
博客等级 码龄17年 48粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值