c++ 类方法和普通方法调用有何不同?

三分钟教你如何在IDEA中快速创建工程 目录1. 创建Java工程2. 设置显示常见的视图3. 工程界面显示4. 创建package和class 1. 创建Java工程 1. 打开的初始界面 Create New Project:创建一个新的工程 Import Project:导入一个现有的工程 Open:打开一个已有工程。比如:可以打开 Eclipse 项目。 Check out from VersionControl:可以通过服务器上的项目地址 check out Github上面项目或其他 Git 托管服务器上的项目 2. 新建项目 如果要创建 Web 工程,则需要勾选上面的 Web Application。如果不需 立即下载

本文将简单对比分析普通方法和类方法调用的不同之处,测试环境为 MacOS。

这里不考虑虚函数和类静态方法,有关虚函数的内容,可以看我以前发的文章。

1、域

我们知道如果调用的普通方法在前面没有声明或定义,会导致报错:

image.png

因为 c/c++ 的解析文件的符号顺序是从上往下,所以图中的 test 函数需要挪到 main 函数之前。

而类/结构体这种域结构的,方法的顺序并不重要,不影响解析结果:

image.png

如图,这里的构造方法可以正常地调用到后面的 fun 函数。

1.1、符号解析过程

简单解释下为什么类中解析符号不会出错,拿 LLVM/clang 编译器举例:

When parsing an inline member function declaration/definition, it does full parsing and semantic analysis of the declaration, leaving the definition for later.

Specifically, the body of an inline method definition is lexed and the tokens are kept in a special buffer for later (this is done by Parser::ParseCXXInlineMethodDef).

Once the parser has finished parsing the class, it calls Parser::ParseLexedMethodDefs that does the actual parsing and semantic analysis of the saved method bodies.

At this point, all the types declared inside the class are available, so the parser can correctly disambiguate wherever required.

注意:这里的 inline 不是代表内联,是指函数在 c++ 类/结构体里面。

2、汇编对比调用方式

#include <stdio.h>

class A{
public:
  A(){ classfun(); };

  void classfun(){
    printf("123\n");
  }
};

void normalfun(){
  printf("456\n");
}

int main(){
  A a;
  normalfun();
}

通过 MachOView 发现类方法变成了 indirect symbol:

image.png

接下来要上汇编了,不过不用慌,在指令傍边我会给出注释,看多了就熟悉了,而且大部分指令对我们不重要,关注调用方式就好:

  • main
                     _main:
0000000100007ea4         sub        sp, sp, #0x20                    ; 开辟栈空间
0000000100007ea8         stp        x29, x30, [sp, #0x10]            ; 保存栈底指针和 LR (函数返回地址)
0000000100007eac         add        x29, sp, #0x10                   ; 更新栈底,这几步都是调用约定
0000000100007eb0         sub        x8, x29, #0x1                    ; 取栈下偏移一个字节的地址,因为这个类只有 1 字节大小
0000000100007eb4         mov        x0, x8                           ; 给 x0 保存,用作给函数传参数,即 this 指针
0000000100007eb8         str        x8, [sp]                         ; 先存好这个地址先
0000000100007ebc         bl         __ZN1AC1Ev                       ; A::A()
0000000100007ec0         ldr        x8, [sp]                         ; 加载回这个地址
0000000100007ec4         mov        x0, x8                           ; 再入参调用函数,this 指针
0000000100007ec8         bl         imp___stubs___ZN1A8classfunEv    ; A::classfun()
0000000100007ecc         bl         __Z9normalfunv                   ; normalfun()
0000000100007ed0         movz       w9, #0x0
0000000100007ed4         mov        x0, x9
0000000100007ed8         ldp        x29, x30, [sp, #0x10]
0000000100007edc         add        sp, sp, #0x20
0000000100007ee0         ret
                        ; endp

可以清楚地看到调用类方法时,会准备第一个参数为 this 指针,即对象存储的地址。

这里 A 类对象是在 main 函数栈内存存储的对象,只是在函数调用中传递了这个栈内存地址,因为 main 函数栈一直在,所以用起来像在堆一样。

  • A::A()
                     __ZN1AC1Ev:        // A::A()
0000000100007ee4         sub        sp, sp, #0x20
0000000100007ee8         stp        x29, x30, [sp, #0x10]
0000000100007eec         add        x29, sp, #0x10                 ; 前几句继续调用约定
0000000100007ef0         str        x0, [sp, #0x8]                 ; 保存好对象要放的地址
0000000100007ef4         ldr        x8, [sp, #0x8]                 ; 放到 x8
0000000100007ef8         mov        x0, x8                         ; 又给 x0,其实是要给下面调用函数传参
0000000100007efc         str        x8, [sp]                       ; 再放一份到栈上
0000000100007f00         bl         __ZN1AC2Ev                     ; 又有一个 A::A(),实际是真正的执行体
0000000100007f04         ldr        x8, [sp]
0000000100007f08         mov        x0, x8
0000000100007f0c         ldp        x29, x30, [sp, #0x10]
0000000100007f10         add        sp, sp, #0x20
0000000100007f14         ret
                        ; endp

                     __ZN1AC2Ev:        // A::A()
0000000100007f40         sub        sp, sp, #0x20                  
0000000100007f44         stp        x29, x30, [sp, #0x10]
0000000100007f48         add        x29, sp, #0x10                 ; 前几句继续调用约定
0000000100007f4c         str        x0, [sp, #0x8]
0000000100007f50         ldr        x8, [sp, #0x8]
0000000100007f54         mov        x0, x8
0000000100007f58         str        x8, [sp]
0000000100007f5c         bl         imp___stubs___ZN1A8classfunEv  ; A::classfun()
0000000100007f60         ldr        x0, [sp]
0000000100007f64         ldp        x29, x30, [sp, #0x10]
0000000100007f68         add        sp, sp, #0x20
0000000100007f6c         ret
                        ; endp

A::A() 构造函数被分成了两部分:准备阶段和实际的执行体。

  • imp___stubs___ZN1A8classfunEv
                     imp___stubs___ZN1A8classfunEv:        // A::classfun()
0000000100007f3c         nop                                      
0000000100007f40         ldr        x16, =__ZN1A8classfunEv      ; __ZN1A8classfunEv
0000000100007f44         br         x16                          ; __ZN1A8classfunEv
                        ; endp

普通方法 normalfun() 是直接跳转过去执行,而类方法这里是通过寄存器跳转到 A::classfun() 的地址执行,加了一个间接跳转的操作。

为什么这里给加了个间接跳转的操作呢?我猜测优化级别低可能会是这种情况。

当用了 -O1 优化时,果然是直接调用了;当用了 -O2 优化时,整个类都被优化掉了。

3、小结

好了,得到了一个人尽皆知、教科书上都写着的结论:类方法第一个参数是 this 指针。

4、参考 & 推荐阅读

How Clang handles the type / variable name ambiguity of C/C++

大模型算法岗常见面试题100道 LangChain是一个用于构建和运行大型语言模型应用的开源框架。它提供了一套工具和组件,帮助开发者将大型语言模型(如 GPT-3)与其他工具和API结合,以完成更复杂的任务。1、LangChain包含哪些核心概念?Components:可重用的模块,例如API调用、数据库查询等。Chains:将多个Components链接在一起以完成特定任务的流程。Prompt Templates: 用于指导语言模型生成输出的文本模板。Output Parsers:解析语言模型输出的工具。 阅读详情

相关推荐

保姆级教程:用NovAtel Inertial Explorer 8.7搞定GNSS/INS紧组合后处理(附避坑指南)

本文提供NovAtel Inertial Explorer 8.7的GNSS/INS紧组合后处理全流程教程,涵盖环境准备、数据转换、基站坐标解算及紧组合处理等关键步骤。特别针对零基础用户,分享实用技巧和常见陷阱,帮助高效完成高精度组合导航数据处理,适用于测绘、自动驾驶等领域。

weixin_42614269的博客 279

函数和方法区别

类外叫函数,类内叫方法 在javascript中的解释为: 函数(function)是可以执行的javascript代码块,由javascript程序定义或javascript实现预定义。函数可以带有实际参数或者形式参数,用于指定这个函数执行计算要使用的一个或多个值,而且还可以返回值,以表示计算的结果。 方法(method)是通过对象调用的javascript函数。也就是说,方法也是函数,...

椰汁菠萝 765

基于PWM的汽车仪表步进电机微步驱动优化实践

本文深入探讨了基于PWM的微步驱动技术,用于优化汽车仪表步进电机的运行性能。通过将传统的分步驱动升级为PWM微步细分驱动,利用正弦/余弦查表法合成旋转磁场,有效消除了指针抖动与噪声,实现了指针的平滑、精准运动。文章以STM32和VID2905电机为例,提供了详细的硬件连接、代码实现及高级优化技巧,为提升汽车仪表体验提供了实用方案。

weixin_29169505的博客 511

IntelliJ idea 2018 平台下JNI编程调用C++算法(一)

参考来源: http://blog.sina.com.cn/s/blog_b37338430101f3zq.html http://www.cnblogs.com/xiaocainiao2hao/p/5619862.html https://blog.youkuaiyun.com/aslgm_1/article/details/71514617 【huachao1001的专栏:http://blog...

wsxzhbzl的博客 1万+

【报错】sqli-labs靶场搭建出现“Unable to connect to the database: security”

搭建sqli-labs靶场时,“Unable to connect to the database: security”报错解决

九芒星的博客 2991

【浅读论文-红外和可见光图像融合-CNN】Infrared and Visible Image Fusion using a Deep Learning Framework

1.这篇是2018年ICPR会议上发表的一篇文章1。博主个人觉得这算比较早期在VIF(红外光和可见光图像融合)中加入了深度学习这一模型的论文。2.这篇文章并不是完全使用深度学习的模型,模型主体在图像分解和融合仍然采用传统的方法,只有其中一个分支在特征提取和重建方面用到了VGG网络。3.论文地址4.代码地址本文提出了基于深度学习框架的一种新算法。本文提出使用下述论文中Image fusion with guided filtering2中的分解方法将源图像分解为基本部分和细节内容。

一枚博鼠的博客 1708

idea 查看一个类的子类_C++ 类的使用(三)

“运算符的重载及类的多态”01—运算符的重载C++中对于内置的运算符可以自定义重载,使用关键字operator实现。运算符的重载方式: 1.通过类内重载2.通过全局函数重载class MyPoint{public: MyPoint(double x, double y) :X(x), Y(y) { } double X; double Y; /...

weixin_39554021的博客 369

控制科学与工程(自动化)保研经验【4】——浙大篇

控制科学与工程(自动化)保研经验【4】——浙大篇

Control Everything 1万+

idea 按空格不会换行_这样配置:让你的 IDEA 好用到飞起来

1.设置maven1.在File->settings->搜索maven2.Mavan home directory--设置maven安装包的bin文件夹所在的位置3.User settings file--设置setting文件所在的位置4.Local repository--设置本地仓库2.IDEA 设置代码行宽度1.在File->settings->Editor-&gt...

weixin_39603117的博客 2046

钢结构焊接连接工艺

钢结构焊接连接工艺

wwdoffice950426的博客 61

全志平台camera驱动开发(3)MIPI接口camera驱动开发

详细沟通: 请加video_D 目前开发过的allwinner平台: V5/V3/V3S/V40/T3/T7/A64等 开发过的mipi sensor: sony:imx317、imx477、imx274、imx258、imx286、imx307、imx385等 ov:ov2718、ov12895、ov2710等 ar: ar0238等 国产:gc1024、gc1034...

jzwjzw19900922的博客 9028

idea 找到 方法调用类_c++ 类方法和普通方法调用有何不同?

本文将简单对比分析普通方法和类方法调用的不同之处,测试环境为 MacOS。这里不考虑虚函数和类静态方法,有关虚函数的内容,可以看我以前发的文章。1、域我们知道如果调用的普通方法在前面没有声明或定义,会导致报错:因为 c/c++ 的解析文件的符号顺序是从上往下,所以图中的 test 函数需要挪到 main 函数之前。而类/结构体这种域结构的,方法的顺序并不重要,不影响解析结果:如图,这里的...

weixin_42532938的博客 261

基于Python实现学习进度跟踪系统

{{ task }}: {{ "%.1f"|format(hours) }}小时

寒寒豆豆软件公司的博客 195

python类方法以及类调用实例方法的理解

classmethod类方法 1) 在python中.类方法 @classmethod 是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法。 类方法的第一个参数cls,而实例方法的第一个参数是self,表示该类的一个实例。2) 普通对象方法至少需要一个self参数,代表类对象实例3)类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该...

weixin_33924770的博客 2343

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

android 蓝牙通讯demo

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

【C++多态篇】多态调用和普通调用

多态调用和普通调用 C++中只有两个调用关系,分为普通调用和多态调用。不满足多态调用就是普通调用。 多态必须构成的两个条件 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写(也就是必须满足重写) 必须通过基类的指针或者引用调用虚函数(指针和引用) 多态调用:跟指向的对象有关,传的对象是谁的,调用的就是谁的。 普通调用:和类型有关。类型是什么调用就是什么,类型是Person的,那...

进阶之路 1228

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Android JNI 普通方法和静态方法详解

Android Jni中Java的静态方法和普通方法有什么区别?很多人可能都不清楚,如果想知道的可以了解一下。这个问题很少人会关注,一个是因为很少会静态方法调用JNI或者回调静态的Java方法; 另外一个是因为知道了或者调试过就不难了,所以很多人觉得没必要讲解和记录。 但是对于JNI 的初学者来说静态方法还是比较陌生的,很容易会懵逼的,不知道有啥区别。 本文主要讲解一下上面两种静态方法在JNI中的区别,有兴趣的可以看看。

wenzhi的博客 1772

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

c++调用类成员和属性的几种方法

c++类关于类静态成员和方法和类的普通成员和方法的关系以及区别 下面把静态属性(方法)称为类的属性(方法) ,普通属性(方法)称为对象的属性(方法) 调用类成员和属性的几种方法 /* 调用类成员和属性的几种方法: 类名::属性或方法名 对象.属性或对象名 */ #include&amp;lt;cstdio&amp;gt; #include&amp;lt;iostream&amp;gt; using namesp...

Dch19990825的博客 6477

extern “C“的作用:c语言调用c++代码中普通函数和类的方法教程(符xcode编译器演示demo)

目录前言extern "C"的作用利用extern "C"给c++ 函数做声明,然后再定义函数,让.c文件可以找到c++的函数c语言调用c++中的全局函数,这里不包含类的概念,直接用 前言 这篇教程主要讲解了,c语言,如何调用c++的类和类方法,目的是为了用swift调用c++代码的教程做铺垫,因为swift无法直接调动c++代码,但是可以调用c语言代码和oc代码,所以先做了这个教程.作为swift调用c++代码其中一种方法的铺垫教程 extern "C"的作用 首先这个 extern “C” {}是告诉

小手琴师的博客 2526

IOS 类方法和实例方法

定义 类方法,也称静态方法,在OC中方法名前的修饰符号为:(+),C++中是指用static关键字修改的方法,Swift里是class func。 实例方法,在OC中方法名前的修饰符号为:(-),C++中指不用static关键字修饰的方法,Swift里是普通func。 区别 类方法 1、self指的是Class 2、可直接通过类名调用 3、在程序开始时生成,方法是连续...

weixin_30337157的博客 245

python类:类方法和静态方法

http://面相对象程序设计中,类方法和静态方法是经常用到的两个术语。逻辑上讲:类方法是只能由类名调用;静态方法可以由类名或对象名进行调用。在C++中,静态方法与类方法逻辑上是等价的,只有一个概念,不会混淆。在python中,方法分为三类实例方法、类方法、静态方法。

皮皮blog 3267

python类方法和静态方法

python类方法和静态方法 python类方法和静态方法 C++的静态方法是用static关键字,python j是没用static的。 python中实现静态方法和类方法都是依赖于python的修饰器来实现的。 class MyClass:      def  method(self):             print("method")      @staticmeth

老邓的编程博客 969

关于Python中静态方法、实例方法、类方法、普通方法

从简,不浪费时间,直接用例子讲解: class A(object): def kkk(self,x): #实例方法,类和实例都能访问 print(self,x) @classmethod #加上这句后 classKkk就成为了类方法,类和实例都可以访问, def classKkk(cls,x):

three_co的博客 2448

cpp调用java_c/cpp调用java方法 实例

javaJNI调用c/cpp实例(姊妹篇之一)的续集c/cpp调用java。计划第三篇写一个java安装程序实例(客户端无jre环境的安装包),以解决java程序(软件)安装不方便的问题,使java程序安装也傻瓜化。直接进入正题:完成本实例需要下列工具/环境:1、java环境2、c/cpp编辑器。windows下推荐用vs/vc++,我用的是vs2008。linux下gcc/g++从 C/CPP ...

weixin_39605647的博客 432

C++ - 简化线程调用的好方法 - 传递类普通成员函数指针给线程作为参数

以前启动线程, 每个启动线程的地方都要写好多句话. 唯一不同的地方是要执行的类静态成员函数不同.明显有很多的多余代码实现, 感觉很不好.正好昨天, 做了sqlite3回调函数的改进(sqlite - sqlite3_exec - c++回调函数的处理).里面用到了给回调函数传递类普通成员函数的指针, 和如何用在类回调函数中使用参数中传入的类实例指针调用函数参数传进来的类普通成员函数指针.今天想对启动线程的代码, 也做类似的处理.弄了2个小时, 搞定了.

博客标题不能为空 1357
上一篇: MacOS:多架构产物如何融合?
下一篇: CMU C++ Tips 笔记
流左沙
博客等级 码龄11年 46粉丝 55原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值