1. 数据成员指针

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

代码示例:

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

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)); //
printf("&D::foo->ptr: 0x%lX\n", PART1_OF_PTR(pdfoo));
printf("&D::foo->adj: 0x%lX\n", PART2_OF_PTR(pdfoo)); //
printf("&D::bar->ptr: 0x%lX\n", PART1_OF_PTR(pdbar));
printf("&D::bar->adj: 0x%lX\n", PART2_OF_PTR(pdbar)); // D* d = new D();
d->foo();
_ZN1B3fooEv(d); // equal to d->foo()
d->bar();
_ZN1D3barEv(d); // equal to d->bar()
return ;
}

(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)); //
printf("&A::foo->adj: 0x%lX\n", PART2_OF_PTR(pafoo)); //
printf("&B::bar->ptr: 0x%lX, ", PART1_OF_PTR(pbbar)); //
printf("&B::bar->adj: 0x%lX\n", PART2_OF_PTR(pbbar)); //
printf("&C::foo->ptr: 0x%lX, ", PART1_OF_PTR(pcfoo)); //
printf("&C::foo->adj: 0x%lX\n", PART2_OF_PTR(pcfoo)); //
printf("&C::quz->ptr: 0x%lX, ", PART1_OF_PTR(pcquz)); //
printf("&C::quz->adj: 0x%lX\n", PART2_OF_PTR(pcquz)); //
printf("&C::bar->ptr: 0x%lX, ", PART1_OF_PTR(pcbar)); //
printf("&C::bar->adj: 0x%lX\n", PART2_OF_PTR(pcbar)); //
return ;
}

参考:

C++ ABI for Itanium: 2.3 Member Pointers

C/C++杂记:深入理解数据成员指针、函数成员指针的更多相关文章

  1. CPP-基础:函数指针,指针函数,指针数组

    函数指针 函数指针是指向函数的指针变量. 因而“函数指针”本身首先应是指针变量,只不过该指针变量指向函数.这正如用指针变量可指向整型变量.字符型.数组一样,这里是指向函数.如前所述,C在编译时,每一个 ...

  2. C语言函数指针变量和指针函数以及指针数组

    C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址.我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数.然后通过指针变量就可以找到并调用这 ...

  3. 【转载】C/C++杂记:深入理解数据成员指针、函数成员指针

    原文:C/C++杂记:深入理解数据成员指针.函数成员指针 1. 数据成员指针 对于普通指针变量来说,其值是它所指向的地址,0表示空指针.而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始 ...

  4. 《C++ Primer》之指向函数的指针

    函数指针是指指向函数而非指向对象的指针.像其他指针一样,函数指针也指向某个特定的类型.函数类型由其返回类型以及形参表确定,而与函数名无关: // pf points to function retur ...

  5. C++中的指针,指针函数和函数指针

    指针是C或C++中的一大难题,因此弄懂指针对C和C++的学习有很大的帮助,最近一直在研究指针,因此写一篇随笔把心得记录一下. 简单来说指针也是一种变量,只不过指针变量所存储的不是我们直观上看到的,而是 ...

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

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

  7. C++函数指针与指针函数干货

    C++要是不常用,相信过四天你的指针函数与函数指针的概念就该忘个精光. 其实只要记住谁在后面谁就是哪个本质. 先了解下指针数组与数组指针吧 数组指针 就是指向数组的指针,它表示的是一个指针,它指向的是 ...

  8. C/C++函数指针,指针函数的用法,用处

     先看函数指针 int func2(int x); /* 声明一个函数 */ int (*q2) (int x); /* 声明一个函数指针 */ q2=func2;    /* 将func函数的首地址 ...

  9. c++父类指针强制转为子类指针后的测试(帮助理解指针访问成员的本质)(反多态)

    看下面例子: #include "stdafx.h" #include <iostream> class A {  //父类 public: void  f()   / ...

随机推荐

  1. 团队作业(五)-笔记app top5

    在互联网快速发展的情况下,各个行业的软件层出不穷,五花八门.各个行业都有相当多的软件介入其中,在如此多的软件之中,便有了相当激烈的竞争角逐.今天我们十五万的总冠军就着笔记APP行业中位列top 5的软 ...

  2. [转帖] 外部访问k8s 里面pod的方式方法

    https://jimmysong.io/posts/accessing-kubernetes-pods-from-outside-of-the-cluster/ 从外部访问Kubernetes中的P ...

  3. OneZero第二周第三次站立会议(2016.3.30)

    会议时间:2016年3月30日  13:00~13:20 会议成员:冉华,张敏,王巍,夏一鸣. 会议目的:汇报前一天工作,全体成员评论,确定会后修改内容或分配下一步任务. 会议内容: 1.前端,完成功 ...

  4. DataGridView列标题居中,内容居中

    //列标题居中 dataGridView1.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleC ...

  5. images & Skeleton

    images & Skeleton image placeholder & loading ... skeleton.css https://github.com/dhg/Skelet ...

  6. github如何添加新的分支

    需求:甲建立分支分给乙步骤:在甲创建的项目仓库里边点右上角的按钮 就可以通过名字搜索了. 在乙的右上角 再选中Repositories就可以看到所有的 存储库 了

  7. 【题解】 Test 买水的ACX(套路)

    题目描述: ACX在××信竞组学会信息竞赛,但是他的同学都很巨,于是要他去买水,结果来到某个买水的商店(奇奇怪怪的商店). 一天,ACX买了 N 个容量可以认为是无限大的瓶子,初始时每个瓶子里有 1  ...

  8. from表单文件上传后页面跳转解决办法

    from表单上传文件,路径跳转后,又不能转发回来. 本人的一个解决办法是.返回一段html代码,浏览器解析后后退一步,回到原来的页面并刷新. return "<html>< ...

  9. Openstack 网络服务 Neutron计算节点部署(十)

    Neutron计算节点部署 安装组件,安装的服务器是192.168.137.12 1.安装软件包 yum install -y openstack-neutron-linuxbridge ebtabl ...

  10. Hadoop生态圈-Flume的主流Sinks源配置

    Hadoop生态圈-Flume的主流Sinks源配置 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客只是配置的是Flume主流的Sinks,想要了解更详细的配置信息请参考官 ...