继承与多态
c语言结构体能保证第一个成员永远都是位于起始地址。而由于内存对齐(一种类型的起始地址必须是该类型长度的整数倍),其他紧邻的成员地址未必连续。
父类总是结构体的第一个成员 ,这样在强制类型转换截取相应大小时得到的总是一个完整的结构体。如
1 2 3 4 5 6 7 8 9 10 11
| struct father{ int age; char* name; ... }; struct son{ father parent; int age; char* name; ... };
|
安全转换:son->father->son
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 0 ┌──────────┐ │ │ │ father │ 0 ┌──────────┐ │ │ cast to father │ │ 10 ├──────────┤ ───────────────► │ father │ │ │ [0,10] │ │ │ son │ 10 └──────────┘ │ │ \ \ 20 └──────────┘ \ son \ 20 \----------\
0 ┌──────────┐ 0 ┌──────────┐ │ │ │ │ │ father │ │ father │ cast to son │ │ │ │ ──────────────► 10 ├──────────┤ 10 └──────────┘ [0,20] │ │ \ \ │ son │ \ son \ │ │ 20 \----------\ 20 └──────────┘
|
不安全转换father->son
或转换成无继承关系的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 0 ┌──────────┐ 0 ┌──────────┐ │ │ │ │ │ father │ │ father │ cast to son │ │ │ │ ──────────────► 10 ├──────────┤ 10 └──────────┘ [0,20] │ │ │ ????? │ │ │ 20 └──────────┘
0 ┌──────────┐ │ │ │ father │ 0 ┌──────────┐ │ │ cast to mother │ │ 10 ├──────────┤ ───────────────► │ ?????? │ │ │ [0,15] │ │ │ son │ 15 └──────────┘ │ │ 20 └──────────┘
|
如果不计后果的类型转换,如将会得到一块不合法的空间,访问将导致segment fault。所以正如多数oop语言都有这么一个概念:子类是父类,但父类不是子类。
纯c中没有安全转换这方面的检测。所以实现多态最基本要做是就是实现一种机制来判断和管理安全的类型转换。
这就需要使用额外的结构来管理类了。
类管理
可以使用特殊结构对类进行描述,如:描述其父类名称,构造函数,析构函数等信息
1 2 3 4 5 6 7 8 9 10 11 12 13
| struct father_class{ .name = "father" .parent = "base", .instance_init = *func, .... };
struct son_class{ .name = "son", .parent = "father", .instance_init = *func, .... };
|
可以使用哈希表保存这些类描述信息,当son类对象要转换时就可以通过哈希表找到对应描述,来判断父类,从而保证安全类型转换。也可以通过哈希表,在类对象需要构造时找到对应的构造方法等等。