// ============================================================
// vptr 排布 / this 指针调整 实测探针
//   对应文档：05-继承多态与虚函数.md 第 8 章 Q2-补
//
// 【怎么编译运行】
//   Windows + MSVC（VS2022）：先打开 "x64 Native Tools Command Prompt for VS 2022"，然后：
//       cl /nologo /utf-8 /std:c++17 /EHsc /O2 vptr_probe.cpp && vptr_probe.exe
//   想直接看对象布局（最权威）：
//       cl /nologo /utf-8 /std:c++17 /d1 reportAllClassLayout /Zs vptr_probe.cpp
//   Linux / macOS：
//       g++ -std=c++17 -O2 -o vptr_probe vptr_probe.cpp && ./vptr_probe
//       g++ -fdump-lang-class -c vptr_probe.cpp     # 看 GCC 的布局 dump
// ============================================================
#include <cstdio>
#include <cstddef>
#include <cstring>
#include <typeinfo>

class A { public: virtual void fa() {} int a = 0; };
class B { public: virtual void fb() {} int b = 0; };
class C : public A, public B { public: int cc = 0; };

// 只有虚函数、没有数据成员的"接口类"：此时两个 vptr 才会相邻
class IA { public: virtual void x() {} };
class IB { public: virtual void y() {} };
class Impl : public IA, public IB { public: int z = 0; };

int main() {
    C c;
    C* pc = &c;
    A* pa = pc;
    B* pb = pc;

    std::printf("sizeof(A)=%zu  sizeof(B)=%zu  sizeof(C)=%zu  alignof(C)=%zu\n",
                sizeof(A), sizeof(B), sizeof(C), alignof(C));
    std::printf("\n[C 对象里的偏移]\n");
    std::printf("  offsetof(C, a)  = %2zu   (= A 子对象起始 + 8)\n", offsetof(C, a));
    std::printf("  offsetof(C, b)  = %2zu   (= B 子对象起始 + 8)\n", offsetof(C, b));
    std::printf("  offsetof(C, cc) = %2zu\n", offsetof(C, cc));

    std::printf("\n[三个指针的数值]\n");
    std::printf("  pc = %p\n", (void*)pc);
    std::printf("  pa = %p   (pc -> pa 调整 = %td)\n", (void*)pa, (char*)pa - (char*)pc);
    std::printf("  pb = %p   (pc -> pb 调整 = %td)\n", (void*)pb, (char*)pb - (char*)pc);

    std::printf("\n[比较语义]\n");
    std::printf("  (void*)pa == (void*)pb  -> %d   (数值比较：差 16，所以 false)\n",
                (int)((void*)pa == (void*)pb));
    std::printf("  (void*)pa == (void*)pc  -> %d\n", (int)((void*)pa == (void*)pc));
    // 注意：A* 与 B* 不能直接比较（不同继承分支，MSVC 报 C2446）。
    // 想让标准保证"相等"，先把两边转成同一个类型（这里都转回 C*）：
    std::printf("  static_cast<C*>(pa) == static_cast<C*>(pb) -> %d\n",
                (int)(static_cast<C*>(pa) == static_cast<C*>(pb)));
    std::printf("  static_cast<C*>(pb) - pc = %td   (反方向调整)\n",
                (char*)static_cast<C*>(pb) - (char*)pc);

    // 字节扫描：vptr 存的是只读段地址，高 4 字节不会是 0
    std::printf("\n[字节扫描：哪些偏移放着 vptr]\n");
    const unsigned char* p = reinterpret_cast<const unsigned char*>(pc);
    for (size_t off = 0; off + 8 <= sizeof(C); off += 8) {
        unsigned long long v = 0;
        std::memcpy(&v, p + off, 8);
        std::printf("  +%2zu: 0x%016llx  %s\n", off, v, (v >> 32) != 0 ? "<-- vptr" : "(数据/填充)");
    }

    std::printf("\n[接口类多继承：没有数据成员时 vptr 才相邻]\n");
    Impl im;
    IA* ia = &im;
    IB* ib = &im;
    std::printf("  sizeof(Impl) = %zu   ia -> ib 调整 = %td   z 在偏移 %zu\n",
                sizeof(Impl), (char*)ib - (char*)ia, offsetof(Impl, z));

    std::printf("\n[typeid 靠 vptr 找到类型信息]\n");
    std::printf("  typeid(*pa).name() = %s\n", typeid(*pa).name());
    std::printf("  typeid(*pb).name() = %s\n", typeid(*pb).name());
    return 0;
}