{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/",
  "atlas": "https://www.pystone.net/?node=cpp-to-assembly-oop-implementation#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation.md",
  "context": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:cpp-to-assembly-oop-implementation",
  "slug": "cpp-to-assembly-oop-implementation",
  "title": "从C++到汇编：面向对象如何落地",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "从C++到汇编：面向对象如何落地",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "6f5aa554c438597c192f8306f49b9b145fbfb488ffa847ef7d0a3f7a290ebd37",
  "assets": [],
  "headings": [
    {
      "depth": 1,
      "text": "从C++到汇编：面向对象如何落地",
      "anchor": "从c到汇编面向对象如何落地",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#%E4%BB%8Ec%E5%88%B0%E6%B1%87%E7%BC%96%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E5%A6%82%E4%BD%95%E8%90%BD%E5%9C%B0"
    },
    {
      "depth": 2,
      "text": "1  编译流水线全景",
      "anchor": "1-编译流水线全景",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#1-%E7%BC%96%E8%AF%91%E6%B5%81%E6%B0%B4%E7%BA%BF%E5%85%A8%E6%99%AF"
    },
    {
      "depth": 2,
      "text": "2  对象物理布局（Derived 单继承，含虚函数）",
      "anchor": "2-对象物理布局derived-单继承含虚函数",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#2-%E5%AF%B9%E8%B1%A1%E7%89%A9%E7%90%86%E5%B8%83%E5%B1%80derived-%E5%8D%95%E7%BB%A7%E6%89%BF%E5%90%AB%E8%99%9A%E5%87%BD%E6%95%B0"
    },
    {
      "depth": 2,
      "text": "3  关键符号与 vtable 片段",
      "anchor": "3-关键符号与-vtable-片段",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#3-%E5%85%B3%E9%94%AE%E7%AC%A6%E5%8F%B7%E4%B8%8E-vtable-%E7%89%87%E6%AE%B5"
    },
    {
      "depth": 2,
      "text": "4  构造函数落地（避免覆盖的两步 vptr 写入）",
      "anchor": "4-构造函数落地避免覆盖的两步-vptr-写入",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#4-%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E8%90%BD%E5%9C%B0%E9%81%BF%E5%85%8D%E8%A6%86%E7%9B%96%E7%9A%84%E4%B8%A4%E6%AD%A5-vptr-%E5%86%99%E5%85%A5"
    },
    {
      "depth": 2,
      "text": "5  虚函数调用 = “对象 → vptr → 间接 call”",
      "anchor": "5-虚函数调用-对象-vptr-间接-call",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#5-%E8%99%9A%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8-%E5%AF%B9%E8%B1%A1-vptr-%E9%97%B4%E6%8E%A5-call"
    },
    {
      "depth": 3,
      "text": "在 main() 内的 p->foo() 反汇编",
      "anchor": "在-main-内的-p-foo-反汇编",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#%E5%9C%A8-main-%E5%86%85%E7%9A%84-p-foo-%E5%8F%8D%E6%B1%87%E7%BC%96"
    },
    {
      "depth": 2,
      "text": "6  delete 触发虚析构",
      "anchor": "6-delete-触发虚析构",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#6-delete-%E8%A7%A6%E5%8F%91%E8%99%9A%E6%9E%90%E6%9E%84"
    },
    {
      "depth": 2,
      "text": "7  目标文件到机器码",
      "anchor": "7-目标文件到机器码",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#7-%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6%E5%88%B0%E6%9C%BA%E5%99%A8%E7%A0%81"
    },
    {
      "depth": 2,
      "text": "8  成本与扩展",
      "anchor": "8-成本与扩展",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#8-%E6%88%90%E6%9C%AC%E4%B8%8E%E6%89%A9%E5%B1%95"
    },
    {
      "depth": 2,
      "text": "9  自行验证",
      "anchor": "9-自行验证",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#9-%E8%87%AA%E8%A1%8C%E9%AA%8C%E8%AF%81"
    },
    {
      "depth": 3,
      "text": "结语",
      "anchor": "结语",
      "citation": "https://www.pystone.net/notes/cpp-to-assembly-oop-implementation/#%E7%BB%93%E8%AF%AD"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:programming-languages-and-runtimes",
      "title": "编程语言与运行时",
      "url": "https://www.pystone.net/notes/programming-languages-and-runtimes/",
      "atlas": "https://www.pystone.net/?node=programming-languages-and-runtimes#knowledge-atlas",
      "label": "编程语言与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "本层中的“从C++到汇编：面向对象如何落地”导航项",
      "citation": "https://www.pystone.net/notes/programming-languages-and-runtimes/#%E6%9C%AC%E5%B1%82"
    }
  ],
  "contentMarkdown": "# 从C++到汇编：面向对象如何落地\n\n\n```cpp\n// hello.cpp\n#include <iostream>\n\nclass Base {\npublic:\n    int x;                    // 数据成员\n    Base(int v = 0) : x(v) {} // 构造函数\n    virtual void foo();       // 虚成员函数\n    virtual ~Base();          // 虚析构\n};\n\nclass Derived : public Base {\npublic:\n    int y;\n    Derived(int v1, int v2) : Base(v1), y(v2) {}\n    void foo() override;      // 重写\n};\n\nvoid Base::foo()   { std::cout << \"Base \"   << x << '\\n'; }\nBase::~Base()      { std::cout << \"~Base\\n\"; }\nvoid Derived::foo(){ std::cout << \"Derived \"<< y << '\\n'; }\n\nint main() {\n    Base* p = new Derived(1, 2); // 多态：基类指针指向派生对象\n    p->foo();                    // 动态分派\n    delete p;                    // 触发虚析构\n    return 0;\n}\n```\n\n## 1  编译流水线全景\n\n| 阶段     | 产物                  | 说明（与 OO 相关的要点）                                |\n| ------ | ------------------- | --------------------------------------------- |\n| 预处理    | `hello.i`           | 展开头文件、宏                                       |\n| **编译** | `hello.s`           | 语法/语义分析 → **生成 vtable 常量、构造/析构函数、thunk**；完成优化 |\n| 汇编     | `hello.o`           | 把 `.s` 汇编为机器码 + 重定位表                          |\n| **链接** | `a.out` (ELF)       | 合并段、解析外部符号；各 vtable 落在 `.rodata`              |\n| **加载** | 运行时 ELF 加载器 `ld.so` | 把节映射入虚拟地址，做最后重定位；执行 `.init_array` 里的静态构造      |\n| 运行     | CPU                 | 指令流 + 对象内 vptr 间接跳转实现面向对象                     |\n\n---\n\n## 2  对象物理布局（Derived 单继承，含虚函数）\n\n```cpp\n偏移  大小  成员 / 隐含字段\n----- ----  -----------------------------\n0     8     vptr → _ZTV7Derived + 16\n8     4     Base::x\n12    4     padding (保持 y 对齐)\n16    4     Derived::y\n20    4     padding (整体 8 字节对齐)\n```\n\n> * 只要类层次里**某处**出现虚函数，最派生对象必有 **单一 vptr**（单继承）。\n> * vtable 前两槽是 RTTI (`typeinfo`)；+16 跳过这两槽即第一虚函数地址。\n\n---\n\n## 3  关键符号与 vtable 片段\n\n```asm\n# 从C++到汇编：面向对象如何落地\n_ZTV4Base:      .quad 0          # [–2] (填充，供实现内部使用)\n                .quad _ZTI4Base  # [–1] RTTI\n                .quad _ZN4Base3fooEv    # [0]  Base::foo\n                .quad _ZN4BaseD1Ev      # [1]  Base::~Base (non-deleting)\n                .quad _ZN4BaseD0Ev      # [2]  Base::~Base (deleting)\n\n_ZTV7Derived:   .quad 0\n                .quad _ZTI7Derived\n                .quad _ZN7Derived3fooEv # 覆写 foo\n                .quad _ZN7DerivedD1Ev\n                .quad _ZN7DerivedD0Ev\n```\n\n---\n\n## 4  构造函数落地（避免覆盖的两步 vptr 写入）\n\n```asm\n## Derived::Derived(int, int) – 伪反汇编\n_ZN7DerivedC2Eii:\n    push   %rbp\n    mov    %rbp, %rsp            # rdi=this, esi=v1, edx=v2\n\n## (1) 调用 Base 子对象构造 —— 写入 Base vptr & 初始化 x\n    mov    %esi, %esi            # 参数映射\n    mov    %rdi, %rdi\n    call   _ZN4BaseC2Ei          # Base::Base(int)\n    #  └→ 在 Base 构造中：\n    #       mov $_ZTV4Base+16,(%rdi)      ; vptr = Base\n    #       mov %esi,8(%rdi)              ; x   = v1\n\n## (2) 初始化 Derived 自身成员\n    mov    %edx, 16(%rdi)        # y = v2\n\n## (3) 覆盖 vptr 为 Derived\n    mov    $_ZTV7Derived+16, %rax\n    mov    %rax, (%rdi)\n\n    pop    %rbp\n    ret\n```\n\n> **为什么一定要先写 Base 然后再写 Derived？**\n> C++ 规则规定：\n>\n> * 构造基类阶段，对象“身份”是 Base；若此时调用虚函数必须静态绑定到 Base 实现。\n> * 只有所有基类构造完毕，才进入派生阶段并把 vptr 最终定向 Derived。\n\n---\n\n## 5  虚函数调用 = “对象 → vptr → 间接 call”\n\n### 在 `main()` 内的 `p->foo()` 反汇编\n\n```asm\n## rdi = p\n    mov    (%rdi), %rax      # rax = *(p) = vptr\n    callq  *(%rax)           # 间接跳转到 vtable[0] → Derived::foo\n```\n\n* 比直接 `call _ZN7Derived3fooEv` 多一次 **内存读取**（取表），即动态分派成本。\n\n---\n\n## 6  delete 触发虚析构\n\n```asm\n    mov    (%rdi), %rax          # 取 vptr\n    callq  *(%rax+8)             # 第 1 槽 (index = 1) 是 ~DerivedD1\n    callq  _ZdlPv                # operator delete  由编译器插入\n```\n\n析构链：`Derived::~Derived()` → `Base::~Base()` → `operator delete`。\n\n---\n\n## 7  目标文件到机器码\n\n* 链接阶段把所有 `call _ZN4BaseC2Ei` 之类符号解析为绝对/相对地址。\n* 若函数来自共享库，机器码里是跳转到 **PLT**；首次执行由动态链接器填 **GOT**，后续直达真实地址。\n* 经过加载器重定位后，最终直接执行的就是 x86-64 **机器码字节序列**（`E8 xx xx xx xx` 为 `call` 相对跳转等），CPU 毫无面向对象概念，只按地址取指令。\n\n---\n\n## 8  成本与扩展\n\n| 机制            | 空间成本                     | 时间成本（热点） | 说明     |\n| ------------- | ------------------------ | -------- | ------ |\n| vptr / vtable | 每对象 8 B（单继承）             | 1 次 L1 读 | 支撑动态多态 |\n| 构造双写 vptr     | 0                        | 构造期两次写   | 只发生一次  |\n| RTTI          | 每类 1 条记录                 | –        | 存放在只读段 |\n| 多重继承          | 对象多 vptr；thunk 调整 `this` | < 5 ns/跳 | 额外偏移修正 |\n| 虚继承           | VTT 表、vbptr              | 同上       | 菱形继承场景 |\n\n---\n\n## 9  自行验证\n\n```bash\ng++ -std=c++20 -O1 -g hello.cpp          # 适度优化保留符号\nobjdump -dC a.out | less                 # 反汇编 + 解码 C++ 名字\nreadelf -r a.out | grep ZTV             # 看虚表重定位\n```\n\n聚焦：\n\n* `Derived::Derived` 中 2 次对 `(%rdi)` 的写入；\n* `p->foo` 的 `mov (%rdi), %rax / call *(%rax)`；\n* `.rodata` 段里 `_ZTV*` 表头结构。\n\n---\n\n### 结语\n\n> **“面向对象靠什么落地？”** —— 固定的内存布局 + 隐含指针 + 表查找 + 间接跳转。\n> CPU 依然只懂“读 / 写 / 跳转”；而编译器把高层语义编排成对寄存器和内存的严格约定，从而让机器码在运行时呈现出多态、继承、封装等 OO 行为。\n"
}
