理解数据成员指针、函数成员指针
转自: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.定义 指针函数是指带指针的函数,即本质是一个函数.函数返回类型是某一类型的指针 函数返回值类型 函数名(参数表) int * f(int x,int y); //函数返回值类型是in ...
- 指针,指针函数,指针数组的区别
1.指针 #include<iostream> using namespace std;int main() {int a=2;int *p;p=&a;printf("% ...
- 静态数据成员友元函数
静态数据成员 1. static成员必须在类外定义并初始化 2. static数据成员和函数成员可以通过对象 名引用也可以通过类名引用{Clock c1,c2; c1.count; Clock::co ...
- 结构体自定义指针函数c语言,详解C语言结构体中的函数指针
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.所以,标准C中的结构体是不允许包含成员函数的,当然C++中的结构体对此进行了扩展.那么,我们在C语言的结构体中,只能通过定义函数指针的方式, ...
- c c++ 函数内数组初值_C/C++函数指针与指针函数
关于指针,前面文章C语言指针详解有过介绍,这里主要讨论函数指针和指针函数. 1 什么是指针? 定义:指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量; 上面一个 4GB 的内存可以存放 ...
- 数组指针和指针数组,函数指针和指针函数,常量指针和指针常量,常量引用
一.数组指针和指针数组 1.数组指针(行指针) 首先要知道数组指针是指向数组的指针.所以数组指针本质是个指针,只不过指向一个数组而已.格式为:T (*ptr)[]. 注意:"[]" ...
- C++ 常数据成员、常成员函数和常对象
首先给出一个例子简单理解const修饰数据变量的基本工作原理: #include <stdio.h>int main() {const int a = 250;int *ptr = (in ...
- c/c++ 指针函数 和 函数指针
指针函数:返回指针类型的函数,定义方法如下: 类型标识符 *函数名(参数列表) 函数指针:指向函数入口地址的指针,定义方法如下: 类型标识符 (*指针名称)(形参列表) 下面我们通过一段代码加深我们的 ...
- 分清函数指针和指针函数
分清函数指针和指针函数 1.指向函数的指针(函数指针) 来分析这样一个声明,void (*f) ( );虽然()的优先级高于*,但由于有括号存在,首先执行的是解引用,所以f是一个指针:接下来执行( ) ...
最新文章
- 【风控系统】风控中心—京东基于Spark的风控系统架构实践和技术细节
- CV之IC: 图像描述(Image Captioning) 的简介、使用方法、案例应用之详细攻略
- 详解HashMap的内部工作原理
- java异常统一处理,Controller层的异常统一处理及返回
- RNN循环神经网络(吴恩达《序列模型》笔记一)
- EqualLogic强势增长,戴尔领跑iSCSI中国及香港地区市场
- ZStack 3.6.0发布:支持云主机从KVM云平台在线迁移至ZStack
- 4G智能模组SIM7600CE兼容移远EC20
- 【预测模型】基于遗传算法优化最小二乘支持向量机实现数据分类matlab代码
- SolidWorks 2010 SP0.0 最新下载+序列号 注册机及方法
- linux iso转换成img文件,Ubuntu下将img 转化成iso的号令[Linux安全]
- python如何使用图片做背景_如何在python pptx中设置背景图像
- 电脑壁纸桌面放计算机,电脑壁纸
- linux的cd命名返回上级目录,返回根目录
- 2021最受欢迎开源免费CMS建站系统排行榜
- 2021-2027全球与中国GPS智能手表市场现状及未来发展趋势
- Java写win10激活码_win10系统配置java环境 生成密钥
- 生活 RH阴性血 AB型
- 数据类型转换--Mat与Vector<Point2f>
- Spring Cloud Gateway自定义Filter
热门文章
- 4固定在底部_自建房不搭彩钢棚,4根钢结构撑个玻璃棚遮风挡雨,上面多个露台...
- python批量ping50台服务器_Python小技巧—批量ping的方法
- 芯片如何储存信息_机器视觉检测设备相机的分辨率是如何定义的怎么分类?
- 为什么爬虫用python不用java_为什么常用Python,Java做爬虫,而不是C#C++等
- CtStatement
- elasticsearch源码分析之search模块(client端)
- Apache Ignite——集合分布式缓存、计算、存储的分布式框架
- mysql Navcat触发器生成订单号
- Jquery源码解析-设计理念
- windows10环境下的RabbitMQ安装步骤(图文)