编译器眼中的虚函数

C++ 的动态绑定、虚函数机制、指针类型与指向对象类型之间的关系 场景是否发生动态绑定行为依赖于哪种类型虚函数调用(正常函数体中)✅ 是动态类型(真实对象类型)非虚函数调用❌ 否静态类型(指针/引用类型)构造函数/析构函数中虚函数调用❌ 否当前类(不会多态)动态绑定只发生在虚函数调用上,而且取决于指针/引用所指向对象的真实类型。否则,一切都由指针的静态类型决定。 阅读详情

先看一段代码:

#include <iostream.h>
class A
{
public:
 virtual void Display(){cout<<"class A::Display()"<<endl;}
};

class B:public A
{ public:  
virtual void Test(){cout<<"class B::Test()"<<endl;}  
void Display(){cout<<"class B::Display()"<<endl;}
};

void main()
{
 A *a;  
B b;  
a=&b;
 a->Test();//出错
 a->Display();
}

这段代码在编译时就出错了,提示: 'Test': is not a member of 'A'。a是指向B类对象的一个指针啊,按理说可以调用其虚函数Test()。我们要注意,这个程序在编译时就出错了!让我们站在编译器的角度来看这个问题:

对于编译器而言,指针a与A类对象只有一个区别:调用成员的方式不同,前者是通过“->”,后者是通过“.”

当编译到a->Test();时,编译器发现:唉,不对啊,A类没有这个函数啊。因此就报错了。下面我们对程序进行小小的修改:

#include <iostream.h>
class A
{
public:  
void Test(){cout<<"class A::Test()"<<endl;}
 virtual void Display(){cout<<"class A::Display()"<<endl;}
};

class B:public A
{
public:
 virtual void Test(){cout<<"class B::Test()"<<endl;}  
void Display(){cout<<"class B::Display()"<<endl;}
};

void main()
{  
A *a;  
B b;  
a=&b;  
a->Test();  
a->Display();
}

编译通过,运行结果:
class A::Test()
class B::Display()

我们再站在编译器的角度分析一下:
1.当编译到a->Test();时,编译器发现:恩,A类有这个函数,并且不是虚函数,将此处和class A::Test()静态绑定之;
2.当编译到a->Display();时,编译器发现:恩,A类有这个函数,等等!好小子,你竟然是个虚函数,那我得通过存储在对象中的指向虚函数表的的指针vptr调用你。所以编译器把a->Display();翻译为:a->vptr->Display();在编译阶段,编译器不知道vptr指针指向的地址,因为此时程序还没运行,对象还没有构造出来;只有在程序运行时,b对象被构造出来了,b中才含有vptr。这就是传说中的神秘的动态绑定!!!

再来看看,虚函数最常见的一种用法,这种用法也是在MFC中经常出现的,如果你有《深入浅出MFC》(第二版)这本书,请翻到147页,Command Routing将虚函数的作用发挥到了极致!如果没有,请看下面的代码:
#include <iostream.h>
class A
{
public:  
void Func(){Display();}
 virtual void Display(){cout<<"class A::Display()"<<endl;}
};

class B:public A
{
public: 
 void Display(){cout<<"class B::Display()"<<endl;}
};

void main()
{  
B b;   
b.Func();
}
编译通过,运行结果:
class B::Display()

看看编译器又变了什么魔术,当编译器编译到void Func(){Display();}时,发现Display()是虚函数,所以翻译为:void Func(){this->vptr->Display();},又是动态绑定!因为在编译阶段,编译器并不知道this指向谁,只有在运行时才知道原来指向的是b对象。

对于虚函数表的说明:虚函数表的内容是依据类中的虚函数声明次序,一一填入函数指针。派生类会继承基类的虚函数表,当我们在派生类中改写虚函数时,虚函数表就受到了影响:表中元素所指的函数地址将不再是基类的函数地址,而是派生类的函数地址。

编译器IR优化揭秘:两行代码如何实现百倍性能提升 在编程语言与编译器领域,中间表示(IR)是连接源代码与机器码的关键桥梁,它采用静态单赋值(SSA)形式和控制流图(CFG)等数据结构,为编译器优化提供了统一的抽象层。通过数据流分析和控制流分析,编译器能够在IR层面识别并消除冗余操作,例如将微小函数内联、对临时对象进行标量替换、将循环不变代码外提,从而显著降低抽象开销。这种优化技术在系统级编程(如C++/Rust)和高性能计算场景中价值尤为突出,能够将热循环中的函数调用、对象拷贝等开销彻底消除。本文以“改两行代码提速100倍”为切入点,深入剖析了过早抽象带来 阅读详情

相关推荐

编译器IR优化:避免过早抽象,从两行代码到百倍性能提升

在编译器技术中,中间表示(IR)是连接高级语言与机器码的关键桥梁,它承载了程序的核心语义信息。基于静态单赋值(SSA)形式和控制流图(CFG)的数据流分析,编译器能够在IR层面实施常量传播、死代码消除、函数内联等关键优化,从而显著提升生成代码的执行效率。这些优化技术的价值在于,它们能够自动消除冗余计算、简化控制流,使程序更充分地利用现代CPU的流水线与缓存体系。在游戏引擎、高频交易系统等对性能有极致要求的应用场景中,理解IR优化原理尤为重要。然而,过早抽象(Premature Abstraction)——例

weixin_30301449的博客 425

从编译器角度理解虚函数、静态绑定和动态绑定

虚函数、静态绑定、动态绑定示例代码示例结果对比代码对比结果虚函数静态绑定动态绑定查看虚函数 示例代码 我们首先来看这么一段代码,设计了两个类:Base、Derive; Base类包含一个保护的成员变量:ma; 以及构造函数和两个show方法(一个不带参数,一个带有参数)。 Derive类公有继承Base类:class Derive : public Base; 包含一个私有的成员变量:mb; 以及构造函数和一个show方法(不带有参数)。 class Base { public: Base(int dat

YY_Song 's blogs 563

sdc时钟约束2——多驱动时钟

sdc 多驱动时钟 master_clock

qq_40411444的博客 5529

编译器处理虚函数的原理

1)通常,编译器给类的每个对象添加一个隐藏的成员,这个成员保存了一个指向虚函数表(virtual function table,vtbl)的指针,而虚函数表中保存了类对象进行声明的虚函数的地址。也就是说我们可以通过这个隐藏成员访问虚函数表,进而访问被声明的虚函数的的地址,从而调用虚函数。 2)现在来看基类和派生类虚函数表的区别和联系,首先需要知道,基类和派生类的虚函数表是两个东西,保存在不同位置...

u012359674的博客 1987

openeuler在线/离线安装docker服务

openeuler在线/离线安装docker服务

haixuti7的博客 1071

ADC电路项目1——10bit SAR ADC 设计,smic18工艺,有工艺库,有效位数ENOB为9.8

包括详细仿真文档,原理介绍,完整电路图,仿真参数已设好,可直接使用,在自己的电脑上就可以运行仿真。本设计采用 smic18mmrf CMOS 工艺,使用栅压自举电路的采样开关,Vcm-based 开关。电容开关控制电路,10 位 DFF,最后用 10 位理想 DAC 模块将数字码转换为 10 进制量进。时序上级板采样的 DAC 电容阵列,两级动态比较器,比较器异步时钟,SAR 动态逻辑,DAC。10bit SAR ADC 设计,smic18工艺,有工艺库,有效位数ENOB为9.8。

qq_48407979的博客 814

C++ 虚函数(试验富文本编辑器)

首先我们先复习一下C++的三大特性:封装就是将数据和行为(或功能)有机结合起来,形成一个整体。把数据和处理数据的操作结合形成类,数据和函数都是类的成员。增强安全性和简化编程。对外隐藏实现细节暴露公共接口。外部通过接口来调用。继承:继承就是A类被B类继承,A类为父类,B类为子类。B类继承Al类的所有公共和保护成员数据(属性)和成员函数(方法)。子类可以重新定义父类某些属性,重写父类的某些方法,即覆盖父类的某些属性和方法。使其获得与父类不同的功能。多态:一个接口多种实现状态。

u014136435的博客 414

摩尔线程嵌入式面试题及参考答案(2万字长文)

说一下你对 drm 框架的理解。 DRM(Direct Rendering Manager)是 Linux 系统中用于管理图形显示设备的一个重要框架。 从架构层面来讲,它处于内核空间,主要目的是为用户空间的图形应用程序提供一个统一的接口来访问图形硬件。DRM 包括内核态的驱动模块和用户态的库。内核态的驱动负责和硬件进行交互,比如对显存的管理、硬件寄存器的配置等。用户态的库则方便应用程序利用内核态提供的功能进行图形渲染等操作。 在图形显示的具体流程中,DRM 可以控制显示设备的分辨率、刷

大模型大数据攻城狮的专栏 1229

不同编译器虚函数的实现方式的区别

class base { virtual void f1() {} }; base bs; 问:“为什么bs虚函数表的地址(int*)(&amp;bs)与虚函数f1()地址(int*)*(int*)(&amp;bs) 不是同一个?” 注:题主理解错了,其实(int*)(&amp;bs)是对象bs的地址,即虚函数表的地址的地址。(int*)*(int*)(&amp;bs)才是虚函数表地址...

a3192048的博客 1030

Silly Tavern酒馆ai角色卡与世界书导入超详细教程!

前几天,我在设置 Silly Tavern 的时候,想导入一个喜欢的角色卡,结果发现不少朋友其实已经下载好了角色资源,但却不知道怎么正确导入使用。这篇文章就作为一份完整的新手向教程,一步步教你如何在 Silly Tavern 中导入角色卡、世界书,并实现与 AI 角色的真实互动。如果你是第一次接触这个功能,建议先收藏慢慢看~点击菜单栏上的 图标,打开角色界面。初始状态下,你应该只看到默认角色 Seraphina。你可以先点击她的头像尝试聊天,也可以导入自己喜欢的角色。点击导入按钮,选择你下载好的角色卡(通

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 2万+

个人顶级AI助手OpenClaw(Clawdbot) - 新手快速入门指南

本文详细介绍了个人顶级AI助手OpenClaw(Clawdbot) 的快速入门指南,包括安装部署和使用配置及注意事项

program哲学 5303

C++中虚函数与晚绑定的编译器实现

编译器到底做了什么实现的虚函数的晚绑定呢?我们来探个究竟。编译器对每个包含虚函数的类创建一个表(称为V TA B L E)。在V TA B L E中,编译器放置特定类的虚函数地址。在每个带有虚函数 的类中,编译器秘密地置一指针,称为v p o i n t e r(缩写为V P T R),指向这个对象的V TA B L E。通过基类指针做虚函 数调用时(也就是做多态调用时),编译器静态地插入取得

1244

Buildroot RK3568系统中更改GPU驱动为:panfrost

1:Kernel中开启Panfrost 2:更新mesa 到mesa-24.0.2 3:查看/proc/interrupts 中的panfrost 中断 4:LInux启动日志 5:查看GPU负载

OpenWrt/WLAN/驱动/Android/嵌入式开发总结 2078

编译器眼中的虚函数动态绑定

根据前面的图示,vptr存放在一个对象this指针指向的对象实例中,而对象的this指针的值,只能在运行时才能知道,因此要得到虚函数,那也就得在程序运行的时候了,这个阶段也只能在运行时,无法在编译时静态绑定,所以也就是所谓的动态绑定。综上,动态绑定并不神秘,形象一点说,就是编译时构造了一个绑定虚函数的“公式”,该“公式”在编译的时候确定它的常量系数,在运行的时候得到它的和实际类型相关的动态参数,进行计算后,就能得到实际类型的虚函数指针,是编译器编译时的静态分析和程序运行时的动态查找,它们共同合作的结果。

moter的专栏 735

手把手教你学 NPU 驱动开发--第一部分:基础准备 开发环境搭建:Linux 内核编译与 NPU 仿真平台配置

摘要:本文详细介绍了搭建Linux内核编译与NPU仿真平台的完整流程。主要内容包括:1) 准备ARM64交叉编译工具链;2) 编译支持KGDB调试的Linux内核;3) 构建最小根文件系统;4) 使用QEMU模拟自定义NPU设备;5) 配置KGDB调试环境。文章重点讲解了如何通过修改设备树添加虚拟NPU设备,并提供了开源NPUIP Gemmini作为实验对象。最后展示了简单的"Hello NPU"驱动验证方法,为后续NPU驱动开发奠定了基础。文中还附有常用命令速查表,方便开发者快速搭建调

MHD0815的博客 384

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

[C++]详细解释虚函数到底是什么, 有啥用

虚函数是什么, 为什么要用, 有什么好处? youtube 侯捷老师讲虚函数视频 在语法上, 成员函数之前加virtual关键字就让这个函数变成虚函数 函数的继承不应该从内存角度理解, 而是继承调用权(子类可以调用父类函数), OOP的继承特性需要搭配虚函数才能最大化体现继承的优势. 成员函数从这个角度可以分成三种: a. 普通(非虚函数) b. virtual函数, 希望子类重新定义(over...

ducal90的博客 7285

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

虚函数效率真的低吗?看起来只是多了一次函数调用,为什么都说它效率低呢?| 虚函数 | C++

在程序设计的世界里,有些概念看似简单,却常被误解。虚函数在面向对象编程中作为实现多态性的核心机制,常为初学者所推崇;然而在资深工程师眼中,其所引入的运行时开销与潜在的性能影响,亦常成为讨论与权衡的焦点。

1252

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

虚函数,静态成员函数调用方式

#include "pch.h" #include <iostream> #include <time.h > #include <stdio.h> #include <vector> using namespace std; class MYACLS { public: int m_i; void myfunc(int abc) { //m_i += abc; //这里需要用到this指针,而this指针为空,则会报

qq_38158479的博客 479

005 虚函数详解

#include "stdafx.h"#include "string"#include <iostream>#include "windows.h" using namespace std; class Base {private: virtual void f() { cout << "Base::f" << endl; } virtual void g...

weixin_33726943的博客 70

編譯器看到你的 C++ 類型時,它在嘲笑你

本文以擬人化視角探討 C++ 編譯器在處理程式碼類型系統時的「心理活動」,揭示程式設計師的抽象設計如何在編譯器層面被解讀為複雜甚至可笑的結構。 核心論點 編譯器作為嚴格邏輯系統,對人類程式設計中的「過度設計」、「歷史包袱」和「認知偏差」抱持一種技術性嘲諷態度,這種嘲諷實質上是完美機器邏輯對人類不完美的自然反應。

王同学的博客 766

C++对象切片和对象切片的阻止(纯虚函数让编译器强迫程序猿修改有对象切片的代码)

下面, 我们来看一下C++中最简单的多态程序: #include using namespace std; class Animal { private: int weight; public: virtual void eat() { cout << "eat" << endl; } }; class People : public Animal { private:

认知 行动 坚持 8778

虚成员函数、静态成员函数调用方式

// project100.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include "pch.h" #include <iostream> #include <time.h > #include <stdio.h> #include <vector> using namespace std; class ...

昔拉的博客 407

从编译器IR优化看性能瓶颈:过早抽象如何成为代码效率的隐形杀手

在程序性能优化中,理解编译器如何工作至关重要。编译器并不直接优化源代码,而是将其转换为一种称为中间表示的标准化、平台无关的抽象形式。基于中间表示,编译器通过控制流分析和静态单赋值形式等核心技术,进行数据流分析和模式识别,从而实现诸如循环优化、常量传播和死代码消除等关键优化。这些优化的技术价值在于,它们能够自动将高级语言代码转换为更高效的底层指令,显著提升执行效率。然而,过早引入的抽象层,如不必要的虚函数调用、高阶函数或微小对象封装,会在中间表示层面形成“毛玻璃效应”,模糊数据流,阻碍内联等关键优化,从而成为

weixin_33924770的博客 313

编程小白眼中的C++内存管理

编程小白眼中的C++内存管理

学IT的细胞膜的博客 932

编译器IR优化揭秘:如何通过常量传播与向量化实现百倍性能提升

编译器优化是现代软件开发中提升程序性能的核心技术,其原理基于对代码中间表示(IR)的静态分析和变换。通过数据流分析和控制流分析,编译器能够识别并应用常量传播、循环优化等关键技术,将高级语言高效转换为底层机器码。这些优化的技术价值在于,它们能够在保持代码语义不变的前提下,显著减少运行时开销,提升指令级并行性。在实际应用场景中,特别是在数值计算、图像处理和游戏引擎等对性能敏感的领域,合理利用编译器优化可以带来数量级的性能提升。本文聚焦于常量传播和自动向量化这两个关键优化技术,结合具体代码示例,解析开发者如何通过

weixin_34210740的博客 390

C++如何主宰AI原生系统开发?揭秘Bjarne眼中不可替代的5大技术支柱

掌握C++在AI原生时代的系统级优势,本文深度解读2025全球C++及系统软件技术大会:Bjarne视角:C++在AI原生时代的核心定位,揭示高性能计算、内存控制、零成本抽象等五大技术支柱在AI基础设施中的关键作用,值得收藏。

PixelWander的博客 502
上一篇: 指向派生类对象的基类指针与虚函数
下一篇: 在VC++6.0下构建可直接编辑的列表控件类
yingzai621
博客等级 码龄19年 12粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值