Programming 版 (精华区)
发信人: zhangyan (从来就没有什么救世猪!), 信区: SoftEng
标 题: OO in C - Poly
发信站: 哈工大紫丁香 (2001年03月27日17:46:33 星期二), 站内信件
3.多态性
----------------------------------------------------------------------------
---
通过提供继承方法的新实现,扩展类可重载父类的行为.例如,Object类定义了析构函
数
Object_Des.String类扩展Object,用自己的析构函数StringDes重载了这一行为.假设需
要
删除一个包含一般Object指针的异类包容器[heterogeneous container].因为String从
Object继承而来,一些指针实际指向String对象.若能激发正确实现StringDes来删除Str
ing
对象,代码将是多态的.依靠对象(String)的run-time类,而不是指针(Object)的类,多态
行为
要求方法决定[resolution],这称为动态绑定.
通过在方法决定中引入间接的附加级别,可以在C中有效地实现动态绑定.不要直接调
用
方法(C函数),而使用在类描述器[descriptor]定义的函数指针来调用函数,类描述器可被
每个对象引用.类描述器(有时叫做虚拟表或VTABLE)是对应虚拟函数(有意让子类重载的
方法)
的函数指针的记录.
在前面的例子中,Object类的虚拟destructor的实现如下.Object类的类描述器声明
了
函数指针Des:
struct ObjectClass {
...
void (*Des)(struct Object*);
};
类Object的每个实例都包含一个指向该了描述器的指针(叫做虚拟指针或VPTR):
struct Object {
struct ObjectClass *__vptr;
};
采用如下形式进行虚拟destructor的动态绑定:
(*obj->__vptr->Des)(obj);
其中obj指向Object结构.
注意指针obj在这儿被使用了两次:一次用于决定[resolving]方法,一次作为this参
数.
动态绑定需要两次的内存访问,比直接函数调用多一次.延迟[late]绑定的内存代价: 在
每个
对象中需存储虚拟指针(从Object继承而来),并且每个类需存储一个VTABLE.
类描述器自己可被当做VTABLE类(a class being represented as a VTABLE objec
t)
的单独的实例.因此你可以使用嵌套VTABLEs的技巧来完成虚拟函数的继承.这被封装在宏
VTABLE中.所有类描述器都直接或间接从ObjectClass描述器继承而来,所以都继承了该虚
拟
destructor.
继承使虚拟函数调用的语法有点复杂.一般,你需要upcast对象指针(在Object类),
downcast虚拟指针__vptr(在特定的类描述器).这些操作,以及双对象指针引用,都封装在
VCALL和END_CALL宏中.例如,采用如下的形式调用对象obj的虚拟destructor:
VCALL(obj, Object, Des)END_CALL;
若虚拟函数不只使用this参数,其余的参数应列在END_CALL宏之前.例如:
result = VCALL(obj, FooClass, Foo) ,5 ,i+j END_CALL;
其中obj指针指向FooClass或其子类,虚拟函数Foo在FooClass VTABLE中定义.
虚拟表需通过它们的constructor进行初始化. 由VTABLE constructor执行的初始化
可
分为两步: 拷贝继承的VTABLE; 重载选定的虚函数实现.
第一步由宏BEGIN_VTABLE自动生成.拷贝继承的VATBLE保证:给子类添加新函数不会
破坏
子类, 换句话说,没必要手动修改子类(只需重编译子类代码).除非明确选定一个类来重
载
子类的行为, 继承的实现已足够了.当然,若类声明自己的虚函数,相应的函数指针在该步
骤
不会被初始化.
第二步将虚函数绑定到其实现,提供VMETHOD和IMETHOD宏来帮助其实现.若你不能提
供
给定方法的实现(你想要它是由子类实现的纯虚函数),你仍需用Object_NoIm空实现来初
始
化函数指针.Object_NoIm中断执行(通过失败断言[failing assertion]),在运行时帮助
发现未实现的抽象方法.
如前面所讲,每个对象都保留一个指向类描述器的指针(虚指针),这从Object类继承
而
来.在constructor中对象初始化时,需正确地设置该指针. 这必须在父类constructor调
用
之后完成, 因为父类constructor将设置该指针指向父类VTABLE. 如果正初始化的对象的
VTABLE还未设置,应调用VTBALE constructor.激发VHOOK宏来完成这两步.注意当该父类
constructor作完父类VTABLE同样的事情的时候,整个类层次被正确地初始化.
--
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.170.172]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:4.788毫秒