理解数据成员指针、函数成员指针
数据 函数 理解 指针 成员
2023-09-14 08:58:39 时间
转自:http://www.cnblogs.com/malecrab/p/5572119.html
1. 数据成员指针
对于普通指针变量来说,其值是它所指向的地址,0表示空指针。
而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始地址的偏移值,空指针用-1表示。例:
代码示例:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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;
}
2. 函数成员指针
函数成员指针与普通函数指针相比,其size为普通函数指针的两倍(x64下为16字节),分为:ptr和adj两部分。
(1) 非虚函数成员指针
ptr部分内容为函数指针(指向一个全局函数,该函数的第一个参数为this指针),adj部分始终为0。例:
代码示例:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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;
}
(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。
代码示例:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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;
}
相关文章
- MySQL的group_concat()函数合并多行数据
- 查找数据库中重复的值的数据,having的使用,count(1),sum等聚会函数
- 【COCOS2DX-LUA 脚本开发之十一】C/C++与LUA之间进行数据函数交互以及解决“PANIC: UNPROTECTED ERROR IN CALL TO LUA API (ATTEMPT TO INDEX A NIL VALUE)”的问题
- 转:java操纵主要数据库的lob类型数据
- Oracle 手工建库第三步 创建数据字典
- SAP UI5 数据绑定中的工厂函数
- Python之sklearn:LabelEncoder函数简介(编码与编码还原)、使用方法、具体案例(在数据缺失和test数据内存在新值(train数据未出现过)环境下的数据LE化)之详细攻略
- Python编程语言学习:一行代码利用enumerate函数把纯列表数据转为自带索引的字典数据,字典格式数据应用之key和value相互提取
- ML之SVM:调用(sklearn的lfw_people函数在线下载55个外国人图片文件夹数据集)来精确实现人脸识别并提取人脸特征向量
- TF之DNN:利用DNN【784→500→10】对MNIST手写数字图片识别数据集(TF自带函数下载)预测(98%)+案例理解DNN过程
- 〖Python 数据库开发实战 - MySQL篇⑭〗- 数据表的基本查询与数据分页
- 语音数据集中label的词频统计
- 数据预处理之独热编码(One-Hot编码)【会让特征之间的距离计算更加合理】
- 习题 11.2 将例11.2的程序片断补充和改写成一个完整、正确的程序,用私有继承方式。在程序中应包括输入数据的函数,在程序运行时输入num,name,sex,age,addr的值,程序应输出以上。。
- 【大数据技术基础系列】列式数据库与基于行的数据库存储数据结构
- 【Android 逆向】代码调试器开发 ( ptrace 函数 | 读取进程内存数据 )
- 数据库基础第四章:数据操作
- MATLAB中导入数据:importdata函数
- tf.convert_to_tensor()函数的使用 | 数据类型转换
- Docker拷贝文件和挂载数据卷详解
- LabVIEW用VISA Read函数来读取USB中断数据
- 高中信息技术(Python)必修1 数据与计算 出现的模块和函数
- RFSoC应用笔记 - RF数据转换器 -16- API使用指南之系统设置相关函数
- Kubernetes 数据卷:NFS