C中的动态方法调度 [英] Dynamic method dispatching in C
问题描述
我知道这听起来很傻,而且我知道 C 不是面向对象的语言.
I know it sounds silly and I know that C is not an Object Oriented Language.
但是有没有什么办法可以在 C 中实现动态方法分派?我想过函数指针,但没有完全理解.
But is there any way that dynamic method dispatching can be achieved in C? I thought about function pointers but don't get the entire idea.
我该如何实施?
推荐答案
正如其他人所指出的,在 C 中当然可以实现这一点.它不仅是可能的,而且是一种相当普遍的机制.最常用的例子可能是 UNIX 中的文件描述符接口.对文件描述符的 read()
调用将分派到特定于提供该文件描述符的设备或服务的读取函数(它是一个文件?它是一个套接字?它是其他类型的设备?).
As others have noted, it is certainly possible to implement this in C. Not only is it possible, it is a fairly common mechanism. The most commonly used example is probably the file descriptor interface in UNIX. A read()
call on a file descriptor will dispatch to a read function specific to the device or service that provided that file descriptor (was it a file? was it a socket? was it some other kind of device?).
唯一的技巧是从抽象类型中恢复指向具体类型的指针.对于文件描述符,UNIX 使用包含特定于该描述符的信息的查找表.如果您使用指向对象的指针,则界面用户持有的指针是基本"类型,而不是派生"类型.C 没有继承本身,但它确实保证指向struct
第一个元素的指针等于包含struct代码>.因此,您可以通过使基"的实例成为派生"的第一个成员来使用它来恢复派生"类型.
The only trick is to recover the pointer to the concrete type from the abstract type. For file descriptors, UNIX uses a lookup table that contains information specific to that descriptor. If you are using a pointer to an object, the pointer held by the user of the interface is the "base" type, not the "derived" type. C doesn't have inheritance per se, but it does guarantee that the pointer to the first element of a struct
is equal to the pointer of the containing struct
. So you can use this to recover the "derived" type by making the instance of the "base" be the first member of the "derived".
这是一个带有堆栈的简单示例:
Here's a simple example with a stack:
struct Stack {
const struct StackInterface * const vtable;
};
struct StackInterface {
int (*top)(struct Stack *);
void (*pop)(struct Stack *);
void (*push)(struct Stack *, int);
int (*empty)(struct Stack *);
int (*full)(struct Stack *);
void (*destroy)(struct Stack *);
};
inline int stack_top (struct Stack *s) { return s->vtable->top(s); }
inline void stack_pop (struct Stack *s) { s->vtable->pop(s); }
inline void stack_push (struct Stack *s, int x) { s->vtable->push(s, x); }
inline int stack_empty (struct Stack *s) { return s->vtable->empty(s); }
inline int stack_full (struct Stack *s) { return s->vtable->full(s); }
inline void stack_destroy (struct Stack *s) { s->vtable->destroy(s); }
现在,如果我想使用固定大小的数组实现堆栈,我可以这样做:
Now, if I wanted to implement a stack using a fixed sized array, I could do something like this:
struct StackArray {
struct Stack base;
int idx;
int array[STACK_ARRAY_MAX];
};
static int stack_array_top (struct Stack *s) { /* ... */ }
static void stack_array_pop (struct Stack *s) { /* ... */ }
static void stack_array_push (struct Stack *s, int x) { /* ... */ }
static int stack_array_empty (struct Stack *s) { /* ... */ }
static int stack_array_full (struct Stack *s) { /* ... */ }
static void stack_array_destroy (struct Stack *s) { /* ... */ }
struct Stack * stack_array_create () {
static const struct StackInterface vtable = {
stack_array_top, stack_array_pop, stack_array_push,
stack_array_empty, stack_array_full, stack_array_destroy
};
static struct Stack base = { &vtable };
struct StackArray *sa = malloc(sizeof(*sa));
memcpy(&sa->base, &base, sizeof(base));
sa->idx = 0;
return &sa->base;
}
如果我想使用列表来实现堆栈:
And if I wanted to implement a stack using a list instead:
struct StackList {
struct Stack base;
struct StackNode *head;
};
struct StackNode {
struct StackNode *next;
int data;
};
static int stack_list_top (struct Stack *s) { /* ... */ }
static void stack_list_pop (struct Stack *s) { /* ... */ }
static void stack_list_push (struct Stack *s, int x) { /* ... */ }
static int stack_list_empty (struct Stack *s) { /* ... */ }
static int stack_list_full (struct Stack *s) { /* ... */ }
static void stack_list_destroy (struct Stack *s) { /* ... */ }
struct Stack * stack_list_create () {
static const struct StackInterface vtable = {
stack_list_top, stack_list_pop, stack_list_push,
stack_list_empty, stack_list_full, stack_list_destroy
};
static struct Stack base = { &vtable };
struct StackList *sl = malloc(sizeof(*sl));
memcpy(&sl->base, &base, sizeof(base));
sl->head = 0;
return &sl->base;
}
堆栈操作的实现将简单地将 struct Stack *
转换为它知道应该是什么.例如:
The implementations of the stack operations would simply cast the struct Stack *
to what it knows it should be. For example:
static int stack_array_empty (struct Stack *s) {
struct StackArray *sa = (void *)s;
return sa->idx == 0;
}
static int stack_list_empty (struct Stack *s) {
struct StackList *sl = (void *)s;
return sl->head == 0;
}
当堆栈的用户在堆栈实例上调用堆栈操作时,该操作将分派到vtable
中的相应操作.这个 vtable
由创建函数初始化,并带有与其特定实现相对应的函数.所以:
When a user of a stack invokes a stack operation on the stack instance, the operation will dispatch to the corresponding operation in the vtable
. This vtable
is initialized by the creation function with the functions that correspond to its particular implementation. So:
Stack *s1 = stack_array_create();
Stack *s2 = stack_list_create();
stack_push(s1, 1);
stack_push(s2, 1);
stack_push()
在 s1
和 s2
上都被调用.但是,对于s1
,它会分派到stack_array_push()
,而对于s2
,它会分派到stack_list_push()
代码>.
stack_push()
is called on both s1
and s2
. But, for s1
, it will dispatch to stack_array_push()
, while for s2
, it will dispatch to stack_list_push()
.
这篇关于C中的动态方法调度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!