正如你所见到的,传递给 B 的成员函数的 “this” 值比传递给 A 的成员函数的 “this” 值大 32 个字节 —— 正好是一个类 A 实例的大小。但是当我们在如下函数中,通过一个类 C 对象的地址来调用类 C 的成员方法(可能是 C 自己定义的,也可能是 C 继承自 A 或者 B的)时,会发生什么呢?
void call_by_ptr(const C &obj, void (C::*mem_func)() const) {
(obj.*mem_func)();
}
取决于所调用的具体的成员方法不同,会有不同的 “this” 值被传递进去。但是 “call_by_ptr” 函数本身并不清楚它从第二个形参得到是指向 “foo()” 还是 “bar()” 的函数指针。只有等到这两个函数的地址被引用时(即 “call_by_ptr” 被调用,实参列表被求值时),函数地址才会确定。这就是为什么在成员函数指针里需要并可以保存这样的信息,以指导程序在调用函数成员之前正确的调整 “this” 指针的位置(译者注:this 指针指向的对象需在运行时才能分配出具体地址,而对成员函数施加 “&” 操作符求地址的运算也是在运行时才可进行)。
最后,让我们把所有信息集中起来,放到如下的小程序中,来揭开该特性背后的秘密吧:
#include <iostream>
struct A {
void foo() const {
std::cout << "A's this:\t" << this << std::endl;
}
char pad0[32];
};
struct B {
void bar() const {
std::cout << "B's this:\t" << this << std::endl;
}
char pad2[64];
};
struct C : A, B
{ };
void call_by_ptr(const C &obj, void (C::*mem_func)() const)
{
void *data[2];
std::memcpy(data, &mem_func, sizeof(mem_func));
std::cout << "------------------------------\n"
"Object ptr:\t" << &obj <<
"\nFunction ptr:\t" << data[0] <<
"\nPointer adj:\t" << data[1] << std::endl;
(obj.*mem_func)();
}
int main()
{
C obj;
call_by_ptr(obj, &C::foo);
call_by_ptr(obj, &C::bar);
}
上面的程序输出如下:
------------------------------
Object ptr: 0x7fff535dfb28
Function ptr: 0x10c620cac
Pointer adj: 0
A's this: 0x7fff535dfb28
------------------------------
Object ptr: 0x7fff535dfb28
Function ptr: 0x10c620cfe
Pointer adj: 0x20
B's this: 0x7fff535dfb48
但愿本文把这个问题讲清楚了。
C++ Primer Plus 第6版 中文版 清晰有书签PDF+源代码
将C语言梳理一下,分布在以下10个章节中:
Linux-C成长之路(十):其他高级议题