Pointer to Member

数据成员指针存储的是偏移

#include <iostream>
using namespace std;

struct Foo {
    int x;
    int y;
};

int main() {
    Foo x{2};
    int Foo::* px = &Foo::x;
    int Foo::* py = &Foo::y;
    cout << (unsigned long &)px << endl; // 输出0
    cout << (unsigned long &)py << endl; // 输出4
    cout << sizeof(px) << endl;          // gcc输出8
}

数据成员指针的大小和实现相关。

成员函数指针比一般指针占用更多空间

和编译器相关。gcc 里成员函数指针在 64 位机器下是 16 字节。

如果成员函数不是虚函数:函数地址固定;this 指针偏移也可以通过类型在编译时计算出来。先偏移后调用,应该只需要 8 个字节。

如果成员函数是虚函数:

解释

https://stackoverflow.com/a/12006882/

存储一个偏移难道还不够吗?

MSVC 的更奇怪,多继承指针比单继承指针的体积大:

#include <iostream>
#include <cassert>
using namespace std;

struct A       { void x() {} };
struct B       { void y() {} };
struct C: A, B { void z() {} };
struct D       { int z; };

int main() {
    // x86模式编译
    // static_assert(sizeof(&A::x) == sizeof(&B::y));
    // static_assert(sizeof(&A::x) == 4);
    // static_assert(sizeof(&C::z) == 8);
    // static_assert(sizeof(&D::z) == 4);
    // x64模式编译
    static_assert(sizeof(&A::x) == sizeof(&B::y));
    static_assert(sizeof(&A::x) == 8);
    static_assert(sizeof(&C::z) == 16);
    static_assert(sizeof(&D::z) == 4);
}

使用成员指针访问成员

观察:

#include <iostream>
using namespace std;
struct Foo {
    int x;
    int y;
};

int main() {
    Foo x{2, 7};
    int Foo::* px = &Foo::x;
    int Foo::* py = &Foo::y;
    cout << ((&x) ->* px) << endl; // 2
    cout << (x .* py) << endl;     // 7
}

可以看出 .*->* 是特殊的访问操作符(这里为了体现其特殊性加了空格,实际上倾向于不加空格书写)。