c++指向成员的指针(二)

上一篇文章我们讲解了成员指针的概念及用法,这里我们再深入来探讨一下。

我们知道一般的指针在32位平台上占4个字节,64位平台上占8个字节,但成员指针有点特殊,它不是一般的指针。对于指向数据的成员指针,其大小和一般指针一样,但其含义不太一样。对于指向成员函数的成员指针,在32位平台上它占8个字节,而在64位平台上占16个字节,这个我们可以通过 sizeof 很容易测得。

我这里的平台是64位,所以接下来的研究是基于64位平台的。那成员指针到底有何不同呢?这个在标准中没有多少说明,但在 Itanium C++ ABI里有这样一段解释:

A pointer to data member is an offset from the base address of the class object containing it,represented as a ptrdiff_t. It has the size and alignment attributes of a ptrdiff_t. A NULL pointer is represented as -1.

A pointer to member function is a pair as follows:

ptr:

For a non-virtual function, this field is a simple function pointer. (Under current base Itanium psABI conventions, that is a pointer to a GP/function address pair.) For a virtual function, it is 1 plus the virtual table offset (in bytes) of the function, represented as aptrdiff_t. The value zero represents a NULL pointer, independent of the adjustment field value below.
adj:
The required adjustment tothis, represented as aptrdiff_t.

我给大家翻译一下:

一个指向数据成员的指针,它的类型是 ptrdiff_t,它里面存储的是该数据成员相对对象在内存中地址的偏移,其大小和对齐属性和 ptrdiff_t 一样(8字节)。-1 表示是空指针。

一个指向成员函数的指针拥有两个域(16字节),其含义如下:

ptr:

对于非虚函数,这个域是个简单的函数指针(在当前基本的 Itanium psABI 约定中,这个指针存储函数的地址)。对于虚函数,  其值是 1 加上该函数在虚函数表中的偏移(以字节为单位),其类型也是 ptrdiff_t。0 表示空指针,它和下面的 用于调整的域 adj 的值无关。

adj:

这个域是用来调整 this 指针的,其类型也是 ptrdiff_t。

ptrdiff_t 在内部的定义如下:

typedef long int ptrdiff_t;

看完这个解释后我们应该对成员指针的实现原理有所了解了,也清楚了指针大小为什么是这么多。我想各位对指向数据成员的指针可能问题不大,可能对指向成员函数的指针还有些疑问,下面我们通过代码来观察一下。

假设我们有这样一个类:

class Pairs {
public:
	int a, b;
	int max() const {
		return a > b ? a : b;
	}
	virtual void show() const {
		cout << "a = " << a << ", b = " << b << endl;
	}
};
该类对象在内存中的布局应该是这样子的:

对象在内存中的布局
虚函数表地址(8字节)
存储成员 a(4字节)
存储成员b(4字节)

我将根据这个类写一些测试代码,并附上输出结果。

示例代码一:

        // 测试代码
	int Pairs::*pa = &Pairs::a; // pa 存储 a 的 偏移
	int Pairs::*pb = &Pairs::b; // pb 存储 b 的 偏移
	cout << "sizeof(pa) = " << sizeof(pa) << endl;
	cout << "pa's value = " << *(long *)&pa << endl; 
	cout << "pb's value = " << *(long *)&pb << endl; 
	
	// 输出结果
	sizeof(pa) = 8
	pa's value = 8
	pb's value = 12

示例代码二:

        // 测试代码
	int (Pairs::*pfunc)() const = &Pairs::max;
	long *temp = (long *)&pfunc;
	cout << "sizeof(pfunc) = " << sizeof(pfunc) << endl;
	printf("ptr of pfunc = 0x%08lx\n", temp[0]); // 输出的是 max 函数的地址
	printf("adj of pfunc = 0x%08lx\n", temp[1]);
	
	// 输出结果
	sizeof(pfunc) = 16
	ptr of pfunc = 0x0040ba8c
	adj of pfunc = 0x00000000

示例代码三:

        // 测试代码
	void (Pairs::*pvirtual)() const = &Pairs::show;
	long *temp = (long *)&pvirtual;
	cout << "sizeof(pvirtual) = " << sizeof(pvirtual) << endl;
	printf("ptr of pvirtual = 0x%08lx\n", temp[0]); // 输出的是 show 函数在虚函数表中的偏移
	printf("adj of pvirtual = 0x%08lx\n", temp[1]);
	
	// 输出结果
	sizeof(pvirtual) = 16
	ptr of pvirtual = 0x00000001
	adj of pvirtual = 0x00000000

从代码和注释应该不需要我多解释了。

对于 adj 域的用处,实际上需要在多重继承中才会体现,由于篇幅关系,在此不作讨论,如需要了解可以和我联系,希望此文对你理解成员指针有所帮助。


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值