为什么 C++ 成员函数指针是 16 字节宽的(2)

正如你所见到的,传递给 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++ 设计新思维》 下载见

C++ Primer Plus 第6版 中文版 清晰有书签PDF+源代码

读C++ Primer 之构造函数陷阱

读C++ Primer 之智能指针

读C++ Primer 之句柄类

C语言梳理一下,分布在以下10个章节中:

Linux-C成长之路(一):Linux下C编程概要

Linux-C成长之路(二):基本数据类型

Linux-C成长之路(三):基本IO函数操作

Linux-C成长之路(四):运算符

Linux-C成长之路(五):控制流

Linux-C成长之路(六):函数要义

Linux-C成长之路(七):数组与指针

Linux-C成长之路(八):存储类,动态内存

Linux-C成长之路(九):复合数据类型

Linux-C成长之路(十):其他高级议题

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/d7199c8006c8bbb501edc28284c8a8fd.html