C中的动态方法调度 [英] Dynamic method dispatching in C

查看:27
本文介绍了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()s1s2 上都被调用.但是,对于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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆