0120、安全开发篇(三):运算符重载、继承与多态
返回:C++ 面向对象基础目录 · 安全开发总目录
这一章是 C++ 面向对象最接近逆向分析的一部分:对象布局、继承层次、虚函数表和动态绑定都会在二进制里留下痕迹。学习时既要能写语法,也要能想象对象在内存中的样子。
6.6 - 7.6 运算符重载
运算符重载让自定义类型也能使用 +、==、[]、<< 等语法。它应该让代码更自然,而不是制造难懂的副作用。
1 |
|
| 运算符 | 复盘重点 |
|---|---|
| 算术重载 | 返回新对象还是修改自身 |
| 比较重载 | 相等规则是否清晰 |
| 赋值重载 | 是否处理自赋值和资源释放 |
| 下标重载 | 是否检查边界 |
| 输出重载 | 通常用友元函数实现 |
7.7 继承概念引入
继承表达“is-a”关系。派生类复用基类接口和数据,也可能扩展新行为。
1 | class Animal { |
7.8 继承访问权限
继承方式会影响基类成员在派生类中的可见性。
| 继承方式 | public 成员 | protected 成员 |
|---|---|---|
| public | public | protected |
| protected | protected | protected |
| private | private | private |
7.9 继承构造析构
构造顺序是基类先构造,再派生类构造;析构顺序相反。基类析构函数如果用于多态删除,应该写成 virtual。
8.0 继承基类调用
派生类可以显式调用基类成员:
1 | class Derived : public Base { |
8.1 继承对象赋值
基类对象接收派生类对象时可能发生对象切片,只保留基类部分。复盘多态时要优先使用引用或指针。
8.2 继承友元关系
友元关系不会因为继承自动传递。基类友元不等于派生类友元。
8.3 继承内存布局
派生类对象通常包含基类子对象,再加上自己的成员。逆向时可以通过成员偏移观察继承关系。
| 部分 | 说明 |
|---|---|
| 基类子对象 | 基类成员数据 |
| 派生类成员 | 新增成员数据 |
| 虚表指针 | 有虚函数时通常出现 |
8.4 继承权限限制
权限控制的是源码层面的可访问性。逆向里依然能看到对象内存,但写代码时要尊重封装边界。
8.5 单继承与多继承
单继承布局相对简单;多继承会让对象里出现多个基类子对象,指针转换也可能带来地址调整。
8.6 多继承机制
多继承下,派生类对象可能包含多个虚表指针和多个基类区域。复盘时重点看“当前指针指向对象的哪个子对象”。
8.7 菱形继承弊端
菱形继承会让公共基类被重复继承,可能出现多份基类数据。虚继承能解决重复数据问题,但布局更复杂。
8.8 继承权限以及布局
把继承复盘成两张图:一张是源码关系图,一张是内存布局图。两张图能对上,继承就真正理解了。
8.9 多态触发机制
多态通常需要三个条件:继承、虚函数、基类指针或引用指向派生类对象。
1 |
|
9.0 虚函数地址表
虚函数表保存虚函数入口地址。对象中通常有虚表指针指向虚表。调用虚函数时,程序通过虚表找到真正执行的函数。
9.1 虚析构执行体
基类指针删除派生类对象时,基类析构函数必须是虚析构,否则可能只析构基类部分。
9.2 纯虚函数抽象
纯虚函数让类变成抽象类,不能直接实例化。
1 | class Shape { |
9.3 - 9.5 多态底层原理
多态底层可以抓住三件事:对象地址、虚表指针、虚函数表项。逆向 C++ 时看到间接调用,很可能就和虚表有关。
| 观察点 | 说明 |
|---|---|
| 对象首部 | 可能保存虚表指针 |
| 虚表 | 保存虚函数地址 |
| 间接调用 | 通过表项调用真实函数 |
| 构造析构 | 会设置或调整虚表指针 |
学习检查清单
| 检查项 | 状态 |
|---|---|
| 能写出基本运算符重载并说明返回值设计 | 待复盘 |
| 能说明 public/protected/private 继承差异 | 待复盘 |
| 能画出单继承对象的大致内存布局 | 待复盘 |
| 能解释对象切片和虚析构问题 | 待复盘 |
| 能说明虚表和动态绑定的基本关系 | 待复盘 |






