本文接着使用c实现动态链接与泛型函数
选择器、动态链接和多态
谁来处理消息?构造函数通过new() 调用来提供一个新的未初始化的内存区域:
void *new(const void *_class, ...)
{
const struct Class *class = _class;
void *p = calloc(1, class->size);
assert(p);
*(const struct Class **)p = class;
if (class->ctor) {
va_list ap;
va_start(ap, _class);
p = class->ctor(p, &ap);
va_end(ap);
}
return p;
}
object 开始处的 struct Class 指针相当重要,这就是我们将这个指针在 new() 中初始化的原因:
右边的类型描述符 class 在编译期间初始化,object 在运行期间初始化,虚线箭头插入。
* (const struct Class **) p = class;
p 指向为 object 新分配的内存起始位置, 将p进行强制类型转换,这样 object 的开始部分作为一个指向 struct Class 的指针,设置参数类来作为这个指针的值,接下来如果构造函数是类型描述符的一部分,我们调用它并返回其结果来作为new()的结果,例如作为一个新的 object。
delete() 假设每一个对象,例如每一个非空指针,指向一个类型描述符,这样的目的是如果存在则调用析构函数, self 起到上图中的 p 的作用。我们使用一个局部变量 cp 进行强制类型转换,非常小心地从 self 到它的类型描述符:
void delete(void *self)
{
const struct Class **cp = self;
if (self && *cp && (*cp)->dtor)
self = (*cp)->dtor(self);
free(self);
}
析构函数通过delete(),获取一个 self 参数,如果一个对象不打算销毁,它的析构函数将返回null。
存储在类型描述中的所有其他方法都以类似的方式调用.。在每一种情况下,我们有一个单一的接收对象自己,我们需要路由方法调用通过其描述符:
所有存储在类型描述符中的其他方法都是通过类似的方式调用,在每种情形下简单的接收 self 对象,依据它的描述符来得到方法调用的线路:
int differ(const void *self, const void *b)
{
const struct Class *const *cp = self;
assert(self && *cp && (*cp)->differ);
return (*cp)->differ(self, b);
}
当然,关键部分是假设我们可以在 *self 直接找到类型描述指针。至少目前,我们防范空指针。我们可以把一个”魔术数”放在每个类型的描述符的前面,或者比较 *self 与自身的地址或者所有已知类型描述一个地址范围,但在后面的文章中将介绍,我们可以做得更严重的检查。
differ() 解释了为什么这个调用函数的技术被称为动态链接或者延迟绑定:当我们为任意对象调用 differ() ,只要从适当的类型描述指针开始,函所做的工作实际上尽可能迟地执行。
我们将 differ() 称为一个选择器函数,这是一个多态函数的例子,例如,一个函数可以接收不同类型的参数并依据参数执行不同的操作。一旦我们实现更多的类,同时这些类的类型描述符包含.differ,这样 differ() 就是一个可以提供给任意类的实例的泛型函数。
可以将选择器作为没有动态链接但是行为依然类似泛型函数,因为它们让动态链接函数做它们实际的工作。
泛型函数被很多编程语言内置,例如,Pascal 语言 write() 程序根据不同的参数类型做不同的处理,C语言中的操作符 + 在被整型、指针类型或者浮点型数据调用的时候产生不同的影响。这种现象被称为重载:参数类型与操作名共同决定操作;同样的操作符可以作用在不同的参数类型上产生不同的效果。
这里没有明确的区分:因为动态链接,differ() 的行为就像是一个重载函数,并且 C 编译器可以使得 + 的行为像多态函数–至少对于内置数据类型。然而,C 编译器可以为操作符 + 创建不同的返回类型,但是 differ() 函数必须通常独立于它的实参类型具有相同的返回类型。方法可以是不具备动态链接而是多态的,作为一个例子,考虑一个函数 sizeof(),它返回任何对象的大小:
size_t sizeOf(const void *self)
{
const struct Class *const *cp = self;
assert(self && *cp);
return (*cp)->size;
}
所有对象携带描述符,可以从那里取得大小,注意不同点:
void * s = new(String, "text");
assert(sizeof s != sizeOf(s));
sizeof 是 C 操作符,在编译期间计算并返回它所要求的参数的字节数。sizeOf() 是我们的多态函数,在运行期间返回对象的大小。
Comments