转自:http://www.cnblogs.com/malecrab/p/5572119.html

1. 数据成员指针

对于普通指针变量来说,其值是它所指向的地址,0表示空指针。

而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始地址的偏移值,空指针用-1表示。例:

代码示例:

struct X {int a;int b; }; #define VALUE_OF_PTR(p) (*(long*)&p) int main() { int X::*p = 0; // VALUE_OF_PTR(p) == -1 p = &X::a; // VALUE_OF_PTR(p) == 0 p = &X::b; // VALUE_OF_PTR(p) == 4 return 0; }

View Code

2. 函数成员指针

函数成员指针与普通函数指针相比,其size为普通函数指针的两倍(x64下为16字节),分为:ptr和adj两部分。

(1) 非虚函数成员指针

ptr部分内容为函数指针(指向一个全局函数,该函数的第一个参数为this指针),adj部分始终为0。例:

代码示例:

extern "C" int printf(const char*, ...); struct B { void foo() { printf("B::foo(): this = 0x%p\n", this); } }; struct D : public B { void bar() { printf("D::bar(): this = 0x%p\n", this); } }; void (B::*pbfoo)() = &B::foo; // ptr: points to _ZN1B3fooEv, adj: 0 void (D::*pdfoo)() = &D::foo; // ptr: points to _ZN1B3fooEv, adj: 0 void (D::*pdbar)() = &D::bar; // ptr: points to _ZN1D3barEv, adj: 0 extern "C" void _ZN1B3fooEv(B*); extern "C" void _ZN1D3barEv(D*); #define PART1_OF_PTR(p) (((long*)&p)[0]) #define PART2_OF_PTR(p) (((long*)&p)[1]) int main() { printf("&B::foo->ptr: 0x%lX\n", PART1_OF_PTR(pbfoo)); printf("&B::foo->adj: 0x%lX\n", PART2_OF_PTR(pbfoo)); // 0 printf("&D::foo->ptr: 0x%lX\n", PART1_OF_PTR(pdfoo)); printf("&D::foo->adj: 0x%lX\n", PART2_OF_PTR(pdfoo)); // 0 printf("&D::bar->ptr: 0x%lX\n", PART1_OF_PTR(pdbar)); printf("&D::bar->adj: 0x%lX\n", PART2_OF_PTR(pdbar)); // 0  D* d = new D(); d->foo(); _ZN1B3fooEv(d); // equal to d->foo() d->bar(); _ZN1D3barEv(d); // equal to d->bar() return 0; }

View Code

(2) 虚函数成员指针

ptr部分内容为虚函数对应的函数指针在虚函数表中的偏移地址加1(之所以加1是为了用0表示空指针),而adj部分为调节this指针的偏移字节数。例:

说明:

  • A和B都没有基类,但是都有虚函数,因此各有一个虚函数指针(假设为vptr)。
  • C同时继承了A和B,因此会继承两个虚函数指针,但是为了节省空间,C会与主基类A公用一个虚函数指针(即上图中vptr1),继承自B的虚函数指针假设为vptr2。
  • C没有重写继承自A和B的虚函数,因此在C的虚函数表中存在A::foo和B::bar函数指针(如果C中重写了foo(),则C的虚函数表中A::foo会被替换为C::foo)。
  • C中有两个虚函数指针vptr1和vptr2,相当于有两张虚函数表。
  • A::foo(C::foo)、B::Bar(C::bar)都在虚函数表中偏移地址为0的位置,因此ptr为1(0+1=1)。而C::quz在偏移为8的位置,因此ptr为9(8+1=9)。
  • 当我们使用pc调用C::bar()时,如:“(pc->*pcbar)()”,实际上调用的是B::bar()(即_ZN1B3barEv(pc)),pc需要被转换为B*类型指针,因此需要对this指针进行调节(调节至pb指向的地址),因此adj为8。

代码示例:

extern "C" int printf(const char*, ...); struct A { virtual void foo() { printf("A::foo(): this = 0x%p\n", this); } }; struct B { virtual void bar() { printf("B::bar(): this = 0x%p\n", this); } }; struct C : public A, public B { virtual void quz() { printf("C::quz(): this = 0x%p\n", this); } }; void (A::*pafoo)() = &A::foo; // ptr: 1, adj: 0 void (B::*pbbar)() = &B::bar; // ptr: 1, adj: 0 void (C::*pcfoo)() = &C::foo; // ptr: 1, adj: 0 void (C::*pcquz)() = &C::quz; // ptr: 9, adj: 0 void (C::*pcbar)() = &C::bar; // ptr: 1, adj: 8 #define PART1_OF_PTR(p) (((long*)&p)[0]) #define PART2_OF_PTR(p) (((long*)&p)[1]) int main() { printf("&A::foo->ptr: 0x%lX, ", PART1_OF_PTR(pafoo)); // 1 printf("&A::foo->adj: 0x%lX\n", PART2_OF_PTR(pafoo)); // 0 printf("&B::bar->ptr: 0x%lX, ", PART1_OF_PTR(pbbar)); // 1 printf("&B::bar->adj: 0x%lX\n", PART2_OF_PTR(pbbar)); // 0 printf("&C::foo->ptr: 0x%lX, ", PART1_OF_PTR(pcfoo)); // 1 printf("&C::foo->adj: 0x%lX\n", PART2_OF_PTR(pcfoo)); // 0 printf("&C::quz->ptr: 0x%lX, ", PART1_OF_PTR(pcquz)); // 9 printf("&C::quz->adj: 0x%lX\n", PART2_OF_PTR(pcquz)); // 0 printf("&C::bar->ptr: 0x%lX, ", PART1_OF_PTR(pcbar)); // 1 printf("&C::bar->adj: 0x%lX\n", PART2_OF_PTR(pcbar)); // 8 return 0; }

View Code

转载于:https://www.cnblogs.com/Allen-rg/p/6927477.html

理解数据成员指针、函数成员指针相关推荐

  1. 初论函数指针、指针函数、指针的指针

    一.指针函数 1.定义 指针函数是指带指针的函数,即本质是一个函数.函数返回类型是某一类型的指针 函数返回值类型 函数名(参数表) int * f(int x,int y); //函数返回值类型是in ...

  2. 指针,指针函数,指针数组的区别

    1.指针 #include<iostream> using namespace std;int main() {int a=2;int *p;p=&a;printf("% ...

  3. 静态数据成员友元函数

    静态数据成员 1. static成员必须在类外定义并初始化 2. static数据成员和函数成员可以通过对象 名引用也可以通过类名引用{Clock c1,c2; c1.count; Clock::co ...

  4. 结构体自定义指针函数c语言,详解C语言结构体中的函数指针

    结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.所以,标准C中的结构体是不允许包含成员函数的,当然C++中的结构体对此进行了扩展.那么,我们在C语言的结构体中,只能通过定义函数指针的方式, ...

  5. c c++ 函数内数组初值_C/C++函数指针与指针函数

    关于指针,前面文章C语言指针详解有过介绍,这里主要讨论函数指针和指针函数. 1 什么是指针? 定义:指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量; 上面一个 4GB 的内存可以存放 ...

  6. 数组指针和指针数组,函数指针和指针函数,常量指针和指针常量,常量引用

    一.数组指针和指针数组 1.数组指针(行指针) 首先要知道数组指针是指向数组的指针.所以数组指针本质是个指针,只不过指向一个数组而已.格式为:T (*ptr)[]. 注意:"[]" ...

  7. C++ 常数据成员、常成员函数和常对象

    首先给出一个例子简单理解const修饰数据变量的基本工作原理: #include <stdio.h>int main() {const int a = 250;int *ptr = (in ...

  8. c/c++ 指针函数 和 函数指针

    指针函数:返回指针类型的函数,定义方法如下: 类型标识符 *函数名(参数列表) 函数指针:指向函数入口地址的指针,定义方法如下: 类型标识符 (*指针名称)(形参列表) 下面我们通过一段代码加深我们的 ...

  9. 分清函数指针和指针函数

    分清函数指针和指针函数 1.指向函数的指针(函数指针) 来分析这样一个声明,void (*f) ( );虽然()的优先级高于*,但由于有括号存在,首先执行的是解引用,所以f是一个指针:接下来执行( ) ...

最新文章

  1. 【风控系统】风控中心—京东基于Spark的风控系统架构实践和技术细节
  2. CV之IC: 图像描述(Image Captioning) 的简介、使用方法、案例应用之详细攻略
  3. 详解HashMap的内部工作原理
  4. java异常统一处理,Controller层的异常统一处理及返回
  5. RNN循环神经网络(吴恩达《序列模型》笔记一)
  6. EqualLogic强势增长,戴尔领跑iSCSI中国及香港地区市场
  7. ZStack 3.6.0发布:支持云主机从KVM云平台在线迁移至ZStack
  8. 4G智能模组SIM7600CE兼容移远EC20
  9. 【预测模型】基于遗传算法优化最小二乘支持向量机实现数据分类matlab代码
  10. SolidWorks 2010 SP0.0 最新下载+序列号 注册机及方法
  11. linux iso转换成img文件,Ubuntu下将img 转化成iso的号令[Linux安全]
  12. python如何使用图片做背景_如何在python pptx中设置背景图像
  13. 电脑壁纸桌面放计算机,电脑壁纸
  14. linux的cd命名返回上级目录,返回根目录
  15. 2021最受欢迎开源免费CMS建站系统排行榜
  16. 2021-2027全球与中国GPS智能手表市场现状及未来发展趋势
  17. Java写win10激活码_win10系统配置java环境 生成密钥
  18. 生活 RH阴性血 AB型
  19. 数据类型转换--Mat与Vector<Point2f>
  20. Spring Cloud Gateway自定义Filter

热门文章

  1. 4固定在底部_自建房不搭彩钢棚,4根钢结构撑个玻璃棚遮风挡雨,上面多个露台...
  2. python批量ping50台服务器_Python小技巧—批量ping的方法
  3. 芯片如何储存信息_机器视觉检测设备相机的分辨率是如何定义的怎么分类?
  4. 为什么爬虫用python不用java_为什么常用Python,Java做爬虫,而不是C#C++等
  5. CtStatement
  6. elasticsearch源码分析之search模块(client端)
  7. Apache Ignite——集合分布式缓存、计算、存储的分布式框架
  8. mysql Navcat触发器生成订单号
  9. Jquery源码解析-设计理念
  10. windows10环境下的RabbitMQ安装步骤(图文)