2022年 10月 27日 来源: 新华社微博 字号:默认 超大

这篇讲一下《深度探索C++对象模型》第三章最后没总结的一部分,就是类的成员变量指针。
这里所谓类的成员变量指针就是指绑定某个类的某个成员变量的指针,而不是某个对象的某个成员变量的指针,下面展现了两者的不同:

// test14.cpp
#include <cstdio>
struct Test {
    char c;
    short s;
    int i;
};
int main() {
    Test t = {.c = 1, .s = 2, .i = 3};
    int* pi = &t.i; // 这个指向对象的成员变量的指针,类型为int*
    int Test::* pmi = &Test::i; // 这是指向类的成员变量的指针,类型为int Test::*
    // 类的成员变量指针的使用:
    t.*pmi = 4;         // 通过对象使用
    printf("t.i = %d\n", t.i);
    Test* pt = &t;
    pt->*pmi = 5;       // 通过指针调用
    printf("t.i = %d\n", t.i);
}

类的成员变量的指针表征的是该成员变量在类内的偏移量。
那如何判断一个指向类的成员变量的指针是无效还是有效?通常指针值为0是无效地址,但偏移为0是有效的呀。
在《深度探索C++对象模型》一书中谈到,为了实现上面的功能,向类的成员变量的指针通常会在其偏移量上加1,在使用时再把1减去。即有效的指向类的成员变量的指针是大于0的,这样0值就是无效的了。
那g++是怎么样实现的呢?

// test15.cpp
#include <cstdio>
struct Test {
    char c;
    short s;
    int i;
};
int main() {
    Test t = {.c = 1, .s = 2, .i = 3};
    int Test::* pi_valid = &Test::i;
    int Test::* pi_invalid = nullptr;
}

使用gdb调试如下:
C++对象模型:g++的实现(三)
可见,g++的实现就是直接把无效的指针设置为-1,而不修改偏移量。
好了,除了存取效率之外,到此为止第三章内容基本就说完了,至于效率我就不测试了。