【转载】C/C++杂记:深入理解数据成员指针、函数成员指针
1. 数据成员指针
对于普通指针变量来说,其值是它所指向的地址,0表示空指针。
而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始地址的偏移值,空指针用-1表示。例:
代码示例:
2. 函数成员指针
函数成员指针与普通函数指针相比,其size为普通函数指针的两倍(x64下为16字节),分为:ptr和adj两部分。
(1) 非虚函数成员指针
ptr部分内容为函数指针(指向一个全局函数,该函数的第一个参数为this指针),adj部分始终为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。
代码示例:

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;
}

参考:
C++ ABI for Itanium: 2.3 Member Pointers
【转载】C/C++杂记:深入理解数据成员指针、函数成员指针的更多相关文章
- C/C++杂记:深入理解数据成员指针、函数成员指针
1. 数据成员指针 对于普通指针变量来说,其值是它所指向的地址,0表示空指针. 而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始地址的偏移值,空指针用-1表示.例: 代码示例: str ...
- C++中的static数据成员与static成员函数
本文要点: 1.static成员它不像普通的数据成员,static数据成员独立于该类的任意对象而存在,每个static数据成员是与类关联的对象,并不与该类的对象相关联! aka:每个static数据成 ...
- 转载文章----十步完全理解SQL
转载地址:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程 ...
- C++类中的static数据成员,static成员函数
C++类中谈到static,我们可以在类中定义static成员,static成员函数!C++primer里面讲过:static成员它不像普通的数据成员,static数据成员独立于该类的任意对象而存在, ...
- 【转载】十步完全理解SQL
很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程序语言.甚至是函数语言(尽管有些人认为 SQL 语言也是一种函数式语言) ...
- SQL Server 存储(2/8):理解数据记录结构
在SQL Server :理解数据页结构我们提到每条记录都有7 bytes的系统行开销,那这个7 bytes行开销到底是一个什么样的结构,我们一起来看下. 数据记录存储我们具体的数据,换句话说,它存在 ...
- SQL Server 存储(8/8):理解数据文件结构
这段时间谈了很多页,现在我们可以看下这些页在数据文件里是如何组织的. 我们都已经知道,SQL Server把数据文件分成8k的页,页是IO的最小操作单位.SQL Server把数据文件里的第1页标记为 ...
- c++父类指针强制转为子类指针后的测试(帮助理解指针访问成员的本质)(反多态)
看下面例子: #include "stdafx.h" #include <iostream> class A { //父类 public: void f() / ...
- 走进C++程序世界-------类的定义和使用(数据成员和方法成员,析构函数,构造函数,内联实现)
类的成员简介 在C++中,可以通过声明一个类来穿件一种新的类型.类将一组变量(他们的类型通常不同)和一组相关的函数组合在一起.类可以有各种类型的变量组成,还可以包含其他类对象.成员变量称为数据成员它们 ...
随机推荐
- jq局部打印插件jQuery.print.js(兼容IE8)
/* @license * jQuery.print, version 1.5.1 * (c) Sathvik Ponangi, Doers' Guild * Licence: CC-By (http ...
- PhoneGap API 之事件处理
一. deviceready 事件 1.在使用 PhoneGap 开发应用时,deviceready 事件是非常常用的.这一事件在设备的本地 环境和页面完全加载完成之后才触发 2.注意:此事件一般晚于 ...
- 排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题
常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结 ...
- bzoj4403:序列统计
我好傻啊 题目 先来看看长度只能为\(n\)的情况 那么答案非常显然是\(\binom{m+n-1}{n}\) 其中\(m=R-L+1\) 因为我们要构造一个非降序列,显然可能一个数会被选择多次,组合 ...
- 2016424王启元 Exp6信息搜集与漏洞扫描
一.原理与实践说明 1.实践内容 本实践的目标是掌握信息搜集的最基础技能.具体有: 1.各种搜索技巧的应用 2.DNS IP注册信息的查询 3.基本的扫描技术:主机发现.端口扫描.OS及服务版本探测. ...
- Kali-linux使用社会工程学工具包(SET)
社会工程学工具包(SET)是一个开源的.Python驱动的社会工程学渗透测试工具.这套工具包由David Kenned设计,而且已经成为业界部署实施社会工程学攻击的标准.SET利用人们的好奇心.信任. ...
- Owin+ASP.NET Identity浅析系列(二)扩展用户属性
在今天,读书有时是件“麻烦”事.它需要你付出时间,付出精力,还要付出一份心境.--仅以<Owin+ASP.NET Identity浅析系列>来祭奠那逝去的…… 上一篇博客讲了用户登录注册问 ...
- Ubunru 16.04 kinetic 下安装turtlebot2
默认安装Ubuntu16.04以及ROS kinetic 防止忘记. 1. 准备工作: $ sudo apt-get install python-rosdep python-wstool ros-k ...
- [转]百度地图API详解之地图坐标系统
博客原文地址:http://www.jiazhengblog.com/blog/2011/07/02/289/ 我们都知道地球是圆的,电脑显示器是平的,要想让位于球面的形状显示在平面的显示器上就必然需 ...
- SpringBoot两种读取配置文件的方式
方式一 @Value("${custom.group}") private String customGroup; 方式二 @Autowired private Environme ...