关于c++成员函数指针的声明,类型定义,赋值和调用就不再赘述了,需要了解的朋友可以看这篇文章:

http://www.cppblog.com/colys/archive/2009/08/18/25785.html

写这篇文章的目的在于看到有文章说c++的成员函数指针并不是普通函数指针,现在我要证明,在我的编译环境中,

所看到的c++的成员函数指针就是普通的函数指针。

这里我介绍一下我“看”到的c++成员函数指针。

首先介绍一下我的编译环境:vs2013  win7系统   x86处理器

先看成员函数指针指向非虚成员函数时的情况

现在来看下测试代码

#include "stdafx.h"
#include <iostream>class BaseClass
{
public:BaseClass(){}~BaseClass(){}int calculate(int a, int b){return a - b;}};
class ChildClass : public BaseClass
{
public:ChildClass(){}~ChildClass(){}int calculate(int a, int b){return a + b;}
};typedef int (BaseClass::*Func)(int, int);
int _tmain(int argc, _TCHAR* argv[])
{Func func;func = &(BaseClass::calculate);BaseClass test;printf("%d", (test.*func)(3, 2));system("pause");return 0;
}

上面的代码的main函数中,成员函数指针func指向非虚成员函数BaseClass::calculate的首地址,所以(test.*func)(3, 2)等价于test.calculate(3, 2),printf打印出的结果为1,现在我要将test的类型改为ChildClass,修改后的主函数如下:

int _tmain(int argc, _TCHAR* argv[])
{Func func;func = &(BaseClass::calculate);ChildClass test;printf("%d", (test.*func)(3, 2));system("pause");return 0;
}

现在打印的结果仍为1,(test.*func)(3, 2)等价于test.BaseClass::calculate(3, 2),相当于派生类的对象调用了基类的成员函数,所以在成员函数指针指向非虚成员函数时,其本质就是指向非虚成员函数的首地址。通过反汇编VS生成的目标文件(.obj)亦可证明这一点。

接下来我们来看成员函数指针指向虚函数时的情况

继续看测试代码

#include "stdafx.h"
#include <iostream>class BaseClass
{
public:BaseClass(){}~BaseClass(){}virtual int calculate(int a, int b){return a - b;}};
class ChildClass : public BaseClass
{
public:ChildClass(){}~ChildClass(){}virtual int calculate(int a, int b){return a + b;}
};typedef int (BaseClass::*Func)(int, int);
int _tmain(int argc, _TCHAR* argv[])
{Func func;func = &(BaseClass::calculate);ChildClass test;printf("%d", (test.*func)(3, 2));system("pause");return 0;
}

相比之前,BaseClass::calculate被声明为一个虚函数,成员函数指针fun指向虚函数BaseClass::calculate,(test.*func)(3, 2)等价于test.calculate(3, 2),printf打印结果为5,我们知道c++在调用非静态成员函数时,会传入一个参数——对象的指针this,将其保存在ecx寄存器中,当成员函数需要访问非静态成员变量时,就根据传入的this指针和非静态成员变量相对对象的内存偏移找到存储该成员变量的内存空间, 而this指针指向的地址中,存储着虚函数表的首地址,即对象内存空间的最开始的位置存放着虚函数表的首地址,当调用虚函数时,往往通过传入的this指针找到虚函数表,再通过虚函数表和对应虚函数相对虚函数表的偏移找到对应虚函数的首地址,并跳转到对应虚函数, test.calculate(3, 2)实际调用的函数取决于test中的虚函数表,即取决于传入的this指针。

以上说了这么多,只是证明成员函数指针也具有多态特性。

那当成员函数指针指向虚函数时,它实际指向的是什么地址呢,这里我通过IDA(反汇编工具)查看了使用测试代码生成的目标文件(.obj)的汇编代码。

成员函数指针指向的函数

得出的结论如下:

在windows上用IDA(反汇编器)反编译包含main函数的目标文件(.obj),发现当c++的类成员函数指针引用一个非虚函数时,该成员函数指针指向非虚成员函数的首地址;但当c++的类成员函数指针指向一个虚函数时,该成员函数指向一个编译器临时生成的寻址函数(假设为findAddr)的首地址,findAddr函数只有两条命令
1 从ecx寄存器中取得this指针,再对this指针取内容即虚函数表的首地址放入eax寄存器
2 通过虚函数表的首地址加上偏移取得对应虚函数地址,跳转到对应虚函数
注意,findAddr并不返回,使用jump跳转到对应虚函数,传递虚函数所需参数在调用findAddr之前
同时,我发现this指向地址存储的内容是虚函数表的首地址

[this]                                          ->        虚函数表的地址

[虚函数表的地址]                         ->        第一个虚函数的地址
[虚函数表的地址+sizeof(void*)]    ->        第二个虚函数的地址

....

当链接器链接时,链接器可能会去查虚函数表,生成的可执行文件中将成员函数指针赋值为指向虚函数时,就把所对应的虚函数的首地址直接赋给成员函数指针,所以可能需要通过反汇编目标文件才能看到这个过程。

后来看到有的文章说成员函数指针包含了this指针调整的信息,所占内存空间会大于一个指针的大小,在我的编译环境中并没有发现这种情况,当出现需要调整this指针时,编译器根据调用的成员函数生成调整this指针的信息,但并没有将其存储在成员函数指针的内存空间或其他内存空间中,在目标文件的汇编代码中以立即数的形式表现出来。

转载请注明出处,如有错误与缺漏之处,还请不吝指正。

c++成员函数指针的本质相关推荐

  1. C++成员变量指针和成员函数指针【The semantics of funcitons】

    原文:https://blog.csdn.net/laojiu_/article/details/68946915 (原文有笔误) 1. #include <cstdio> #includ ...

  2. 成员函数指针与高性能的C++委托

    成员函数指针与高性能的C++委托 http://www.cnblogs.com/jans2002/archive/2006/10/13/528160.html Member Function Poin ...

  3. 成员函数指针与高性能的C++委托(中篇)

    成员函数指针与高性能的C++委托(中篇) 撰文:Don Clugston (接上篇) 成员函数指针--为什么那么复杂? 类的成员函数和标准的C函数有一些不同.与被显式声明的参数相似,类的成员函数有一个 ...

  4. C++成员变量指针和成员函数指针

    深度探索C++对象模型这本书还有提到C++类的成员变量指针和成员函数指针,虽然在实际开发中用的不多,但是还是需要理解下. 一:成员变量指针 1.1 非静态成员指针 类成员变量指针,实际上并不是真正意义 ...

  5. 详解函数指针和类成员函数指针

    作者:倾夜·陨灭星尘 一.什么是函数指针? 函数指针,顾名思义即指向函数的指针. 如果要问,为什么能用一个指针指向一个函数呢?我觉得要理解这个问题,以及要理解后面的函数指针和类成员函数指针,没有什么比 ...

  6. 成员函数指针与高性能的C++委托 (Member Function Pointers and the Fastest Possible C++ Delegates)...

    标准C++中没有真正的面向对象的函数指针.这一点对C++来说是不幸的,因为面向对象的指针(也叫做"闭包(closure)"或"委托(delegate)")在一些 ...

  7. 类的成员函数指针和mem_fun适配器的用法

    先来看一个最简单的函数: void foo(int a) {cout << a << endl; } 它的函数指针类型为 void (*)(int); 我们可以这样使用: vo ...

  8. 类成员函数指针的语法

    /*类成员函数指针的语法*/ /*****************************类.h文件************************************/ #if !defined ...

  9. 成员函数指针与高性能的C++委托(三)

    委托(delegate) 和成员函数指针不同,你不难发现委托的用处.最重要的,使用委托可以很容易地实现一个Subject/Observer设计模式的改进版[GoF, p. 293].Observer( ...

最新文章

  1. C语言 · 计算时间
  2. 关于建立完整商业应用软件框架库的设想
  3. 转:Jeff Dean的Stanford演讲
  4. python怎么返回最初_Python 函数为什么会默认返回 None?
  5. java程序如何优化--技巧总结
  6. Nginx FastCGI的运行原理
  7. cocos2d-lua ARPG手机游戏《烈焰遮天》(客户端+服务端+数据库)发布说明
  8. 二元随机变量函数的分布
  9. LeetCode - Easy - 169. Majority Element
  10. 2017.10.1 蚯蚓 思考记录
  11. linux 文件差异备份,Linux使用 tar命令-g参数进行增量+差异备份、还原文件
  12. LINUX操作系统的内核编译内幕详解一
  13. 论文笔记_S2D.62_VIL-SLAM_立体视觉惯性激光雷达同时定位和建图
  14. linux下替代windows的软件列表
  15. 盘点免费好用的5款思维导图工具
  16. 3090显卡 爆显存调试
  17. 硬件:宽带猫(光猫)的基础知识
  18. 香鸡排三部曲:完结篇
  19. 关于MYM码支付系统
  20. Gradle本地化构建技巧之自定义Gradle配置文件

热门文章

  1. CSS—— grid 网格布局
  2. python前端——HTML超文本标记语言、CSS层叠样式表
  3. 数据可视化笔记7 网络数据可视化
  4. 获取checkbox中被选中的值
  5. Navicat-数据库的连接以及使用
  6. matlab axisxy行列关系,在matlab中axis是什么意思
  7. Unity地图分割组合时出现接缝的处理办法
  8. IPv6与ICMPv6
  9. OLE、ActiceX、COM、DLL
  10. 苏菲兔子和魔法师小麦