获取成员函数地址及获取函数地址

2023-10-29



 首先我们定义一个类Ctest,类里面包含三个不同形式的成员函数,静态成员函数statFunc()、动态成员函数dynFunc()和虚拟函数virtFunc()。在main函数中我们利用cout标准输出流分别输出这三个函数的地址,程序如下所示:

#include< iostream>
#include< stdio.h>
using namespace std;

class Ctest
{
public:
 static void statFunc()
 {  cout<< "statFunc"<< endl; }

 void dynFunc()
 {  cout<< "dynFunc"<< endl; }

 virtual void virtFunc()
 {  cout<< "virtFunc"<< endl; }
};

void main()
{
 cout<< "address of Ctest::statFunc:"<<& Ctest::statFunc<< endl;
 cout<< "address of Ctest::dynFunc :"<<& Ctest::dynFunc<< endl;
 cout<< "address of Ctest::virtFunc:"<<& Ctest::virtFunc<< endl;
 while(1);
}

屏幕输出结果如下图所示:


从图中可以看出静态函数的地址显示正常,是一个32位地址值,但是动态函数和虚拟函数的地址都输出1,明显不是地址值。

     为了知道为什么,我们必须分析一下这几种成员函数在运行机制的不同。我们都知道,静态函数是独立于对象的,是类拥有的,所以我们调用静态函数,既可以通过类调用(如Ctest::statFunc())也可以通过对象调用(如Ctest Object; Object.statFunc())。无论是通过类调用还是对象调用,对应的都是同一个函数。

     但是,对于动态函数,只能通过对象来调用。因为我们在动态成员函数中,往往都需要访问对象的成员变,我们知道同一类型的不同对象,它们拥有类中成员变量的不同副本,所以假如动态成员函数由类来调用,我们怎么知道在函数中访问的是哪一个对象的成员变量。另外要说一下,我们说动态函数的调用必须通过对象来调用,是不是说动态成员函数是跟对象绑定的,是不是不同的对象所调用的成员函数就是不同的呢(注意,我们说成员函数不同,是指函数代码存储的地址不同,不是说函数的行为不同),所以我们要输出动态函数的地址,必须通过对象来获取呢,如Ctest Object;& Object.statFunc,我们在编译器上编译一下,可以发现编程出错,原因是对象只能用来调用函数的,并不能用来获取函数的地址。因为我们要获取函数地址,还是得通过类来获取的,动态函数同样是跟类绑定而不是跟对象绑定的。C++调用非静态的成员函数时,采用的是一种__thiscall的函数调用方式。采用这种调用方式,编译器在编译的时候,会在调用的函数形参表中增加一个指向调用该成员函数的指针,也就是我们经常说的this指针。调用的形式类似于Ctest::dynFunc(Ctest* this, otherparam...),在函数体中,涉及到对象的成员变量或者其他成员函数,都会通过这个this指针来调用,从而达到在成员函数中处理调用对象所对应的数据,而不会错误处理其他对象的数据。可见,虽然我们必须通过对象来调用动态函数,但是其实我们访问的都是同一个成员函数。所以我们上面采用&Ctest::dynFunc类名来获取成员函数地址是没错的,动态函数同样是跟类绑定而不是跟对象绑定的。

     出错的原因是,我们输出操作符<<没有对void(__thiscall A::*)()类型重载,编译器将这种类型转换为bool类型,所以输出了1;对于静态函数,其调用方式并非__thiscall,<<有对它的重载,因此类的静态函数可以直接用cout输出函数地址。我们可以用printf输出,因为他可以接收任意类型的参数,包括__thiscall类型,所以我们将cout<< "address of Ctest::dynFunc :"<<& Ctest::dynFunc<< endl;改为printf( "address of Ctest::dynFunc :X\n",& Ctest::dynFunc);输出如下图所示:


从图中可以看书,通过使用printf输出,我们得到了动态函数的地址。那么对于虚拟函数,我们同样采用printf来输出,是不是就可以了呢,我们将cout<< "address of Ctest::virtFunc:"<<& Ctest::virtFunc<< endl;
改为printf( "address of Ctest::virtFunc :X\n",& Ctest::virtFunc);运行的输出如下:


由上图可以看出同样可以得出一个准地址值。

为了验证取到的地址是否正确,我们可以分别定义三个成员函数指针来保存获得的函数地址,然后通过调用函数指针来看输出是否正确,就可以判断取到的地址是否正确了。以下是验证的代码:

#include< iostream>
#include< stdio.h>
using namespace std;

class Ctest
{
public:
 static void statFunc()
 {
  cout<< "statFunc"<< endl;
 }

 void dynFunc()
 {
  cout<< "dynFunc"<< endl;
 }

 virtual void virtFunc()
 {
  cout<< "virtFunc"<< endl;
 }
};

void main()
{
 Ctest Object;
 Ctest* pObject =& Object;
 cout<< "address of Ctest::statFunc:"<<& Ctest::statFunc<< endl;
 printf( "address of Ctest::dynFunc :X\n",& Ctest::dynFunc);
 printf( "address of Ctest::virtFunc:X\n",& Ctest::virtFunc);
 static void (*p_statFunc)();
 void (Ctest::*p_dynFunc)();//注意非静态成员函数指针的定义需指明在那个类的域内
 void (Ctest::*p_virtFunc)();
 p_statFunc =& Ctest::statFunc;
 p_dynFunc =& Ctest::dynFunc;
 p_virtFunc =& Ctest::virtFunc;
 p_statFunc();
 //非静态成员函数指针的调用也与普通函数不同,另外因为.*的优先级比()低,所以需要用括号把左边的操作

 //数括起来,如果写成Object.*p_dynFunc();将无法通过编译

 (Object.*p_dynFunc)();

 (Object.*p_virtFunc)();
 while(1);
}
代码运行之后显示如下,从输出内容可见我们成功调用了对应的成员函数:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

获取成员函数地址及获取函数地址 的相关文章

  • 测试框架pytest教程(4)运行测试

    运行测试文件 pytest q test example py 会运行该文件内test 开头的测试方法 该 q quiet标志使输出保持简短 测试类 pytest的测试用例可以不写在类中 但如果写在类中 类名需要是Test开头 非Test开
  • C/C++中使用Base64编码解码(使用boost库)

    Base64是一种用64个字符来表示任意二进制数据的方法 用记事本打开exe jpg pdf这些文件时 我们都会看到一大堆乱码 因为二进制文件包含很多无法显示和打印的字符 所以 如果要让记事本这样的文本处理软件能处理二进制数据 就需要一个二
  • c++判断硬盘是否连接、修改指定盘符、读取配置文件、获取exe路径

    系统 win7 64 编译器 vs2010 mfc对话框程序 工程名 fixde 语言 c 涉及函数 GetPrivateProfileString 读取配置文件内容 类型为string GetDriveType 获取某驱动器状态 GetV
  • w5500 php,[W5500]搭建属于你的家庭网络实时监控

    图9 OV2640 SVGA模式下图像输出时序图 系统上电后 MCU配置OV2640的工作方式 在OV2640准备好图像后 VSYNC会被拉高一段时间 MCU通过PCLK上升沿中断按字节接收图像数据 接下来我们将对OV2640的初始化配置程
  • 编写测试用例的基本方法之边界值

    一般边界值分析是因为程序开发循环体时的取数可能会因为 lt lt 搞错 比如下面代码 for int i 0 i lt 100 i int j i 1 System out println 循环第 j 次 循环地做某件事情 这里的程序是循环

随机推荐

  • 如何点击按钮把光标定位到想要的el-input中

    1 el inpu
  • C语言-结构体面向对象编程技巧

    Keil4 C51工程网址 https yunpan 360 cn surl yrNkQSrCKyc 一 面向对象 面向对象是软件开发方法 是相对于面向过程来讲的 通过把数据与方法组织为一个整体来看待 从更高的层次来进行系统建模 更贴近事物
  • python3 数据类型归纳

    1 简介 1 1 python3 数据类型 类型 含义 示例 int 整型 1 float 浮点型 1 0 bool 布尔值 True或False complex 复数 a bj string 字符串 abc123 list 列表 a b
  • 别被骗了,win10家庭版MMC是无法创建管理单元的

    今天我的PC 是win10家庭版 无法出现gpedit msc 然后下载gpedit msc 后 又出现MMC无法创建管理单元 家庭版是没有组策略和注册表的 家庭版是没有组策略和注册表的 家庭版是没有组策略和注册表的 重要的事情说三遍 下面
  • 长轮询与长连接

    实现即时通讯主要有四种方式 它们分别是轮询 长轮询 comet 长连接 SSE WebSocket 它们大体可以分为两类 一种是在HTTP基础上实现的 包括短轮询 comet和SSE 另一种不是在HTTP基础上实现是 即WebSocket
  • Spring笔记【黑马】

    Spring day01 今日目标 掌握Spring相关概念 完成IOC DI的入门案例编写 掌握IOC的相关配置与使用 掌握DI的相关配置与使用 1 课程介绍 对于一门新技术 我们需要从为什么要学 学什么以及怎么学这三个方向入手来学习 那
  • 三点估算法评估开发工作量

    概述 开发人员在进行开发工作之前都需要给出一个工作量的评估 以便后续的工作任务可以基于该时间进行排期 大多数开发人员评估工作量主要是基于过往的工作经验拍脑袋决定 并会给自己预留出一定的Buffer时间 这样可能的问题就是过度依赖个人的开发经
  • selenium爬虫_selenium爬虫如何避免对isTrusted属性检测?

    1 前言 各位码友 有两天不见 想小码哥了没 哈哈哈 成都疫情在平静9个月之后 又死灰复燃 目前还未找到确切的源头 提醒各位成都的码友一定注意戴口罩 做好自我防护 相信有关部门的防疫措施 一起共渡难关 好了 今天咱们再继续selenium爬
  • MyBatis---缓存-提高检索效率的利器

    目录 让我们来看看官方文档 缓存 一 一级缓存 1 基本介绍 2 一级缓存 3 一级缓存失效分析 二 二级缓存 1 基本介绍 2 二级缓存快速入门 2 1快速入门 3 注意事项和使用陷阱 三 Mybatis 的一级缓存和二级缓存执行顺序 四
  • C++对象模型之内存区的使用

    对象模型是面向对象程序设计语言的一个重要方面 它会直接影响面向对象语言编写程序的运行机制及对内在的使用机制 因此了解对象模型是进行程序优化 的基础 分析一般意义上程序中的数据在内存中的分布 以及程序使用的不同种类的内存等基本的概念 了解对象
  • 第八章(3) 聚类:DBSCAN和簇评估

    基于密度的聚类寻找被低密度区域分离的高密度区域 传统的密度 基于中心的方法 数据集中特定点的密度通过对该点半径之内的点计数 包括本身 来估计 关键是确定半径 根据基于中心的密度进行点分类 稠密区域内的点 核心点 点的邻域由距离函数和指定半径
  • 微信小程序(十)之消息推送配置(token验证失败的解决方案)

    背景 微信小程序开发 准备使用模板消息做些事情 但是发现需要先在微信公众平台的开发 开发设置 消息推送做配置 然后我们后台人员就开始各种配置 但是一到验证token就报错 很是郁闷 然后各种排查 发现了最终原因 过程和代码如下 很多网站给出
  • datx 开启debug

    1 datax源码编译 编译 mvn U clean package assembly assembly Dmaven test skip true 2 创建mysql测试表 SET FOREIGN KEY CHECKS 0 Table s
  • mysql中 SET autocommit=0 与 START TRANSACTION 的区别

    在MySQL中 SET autocommit 0 指事务非自动提交 自此句命令执行以后 每个SQL语句或者语句块所在的事务都需要显式调用commit才能提交事务 不管autocommit 是1还是0 START TRANSACTION co
  • ssh 配置文件中 maxsessions 与 MaxStartups

    MaxStartups 同时允许几个尚未登入的联机画面 所谓联机画面就是在你ssh登录的时候 没有输入密码的阶段 如下图 maxsessions 同一地址的最大连接数 也就是同一个IP地址最大可以保持多少个链接 转载于 https blog
  • CentOS基础命令大全

    1 关机 立即关机 shutdown h now 立即关机 init 0 立即关机 telinit 0 预约时间关机 shutdown h hours minutes 取消预约关机 shutdown c 重启 shutdown r now
  • 一些关于远程仓库操作的git指令

    1 更换项目所关联的仓库 要先删除目前的远程仓库 然后再添加新的远程仓库 1 git remote rm origin 2 git remote add origin 新的仓库地址 3 git remote v 查看现在的远程仓库 4 gi
  • Hausdorff 距离

    Hausdorff 距离是描述两组点集之间相似程度的一种量度 假设有两组集合 则这两个点集之间的单向 Hausdorff 距离 其中 a b 表示 a 与 b 之间的欧氏距离 h A B 也叫前向 Hausdorff 距离 h B A 也叫
  • Android9.0 mm编译失败:ninja: error: 'xxx', needed by 'xxx', missing and no known rule to make it

    Android系统源码环境下使用mm命令单独编译某一个模块 如果该模块依赖其它模块 可能会报如下错误 解决此问题的方法就是改成mma命令编译 mma命令会构建所需要的关联模块 编译命令简单总结 mm 编译当前目录下的模块 当前目录下要有An
  • 获取成员函数地址及获取函数地址

    首先我们定义一个类Ctest 类里面包含三个不同形式的成员函数 静态成员函数statFunc 动态成员函数dynFunc 和虚拟函数virtFunc 在main函数中我们利用cout标准输出流分别输出这三个函数的地址 程序如下所示 incl