【发布时间】:2012-01-01 21:58:03
【问题描述】:
【问题讨论】:
-
您总是可以通过实现一些 vtable 技术来编写多态的东西。这只是玩弄函数指针来创建合适的间接级别。
标签: c oop polymorphism
【问题讨论】:
标签: c oop polymorphism
这是Nekuromento's second example,我认为这是面向对象 C 的惯用方式:
#ifndef ANIMAL_H_
#define ANIMAL_H_
struct animal
{
// make vtable_ a pointer so they can be shared between instances
// use _ to mark private members
const struct animal_vtable_ *vtable_;
const char *name;
};
struct animal_vtable_
{
const char *(*sound)(void);
};
// wrapper function
static inline const char *animal_sound(struct animal *animal)
{
return animal->vtable_->sound();
}
// make the vtables arrays so they can be used as pointers
extern const struct animal_vtable_ CAT[], DOG[];
#endif
#include "animal.h"
static const char *sound(void)
{
return "meow!";
}
const struct animal_vtable_ CAT[] = { { sound } };
#include "animal.h"
static const char *sound(void)
{
return "arf!";
}
const struct animal_vtable_ DOG[] = { { sound } };
#include "animal.h"
#include <stdio.h>
int main(void)
{
struct animal kitty = { CAT, "Kitty" };
struct animal lassie = { DOG, "Lassie" };
printf("%s says %s\n", kitty.name, animal_sound(&kitty));
printf("%s says %s\n", lassie.name, animal_sound(&lassie));
return 0;
}
这是一个运行时多态性的例子,因为那是方法解析发生的时候。
C1x 添加了泛型选择,这使得通过宏实现编译时多态性成为可能。以下示例摘自 C1x 4 月草案,第 6.5.1.1 节第 5 节:
#define cbrt(X) _Generic((X), \
long double: cbrtl, \
default: cbrt, \
float: cbrtf \
)(X)
用于数学函数的类型通用宏已经在 C99 中通过标题 tgmath.h 提供,但用户无法在不使用编译器扩展的情况下定义自己的宏。
【讨论】:
DOG AND CAT 放在animal.h 中是否约定俗成?我可以想象他们也可以进入各自的标题。
C 中几乎所有运行时多态的实现都将使用函数指针,因此这是基本的构建块。
这是一个简单的例子,当过程运行时行为根据它的参数而改变时。
#include <stdio.h>
int tripple(int a) {
return 3 * a;
}
int square(int a) {
return a * a;
}
void transform(int array[], size_t len, int (*fun)(int)) {
size_t i = 0;
for(; i < len; ++i)
array[i] = fun(array[i]);
}
int main() {
int array[3] = {1, 2, 3};
transform(array, 3, &tripple);
transform(array, 3, &square);
size_t i = 0;
for (; i < 3; ++i)
printf("%d ", array[i]);
return 0;
}
使用函数指针,您可以创建 virtual tables 并使用它来创建将被统一处理但在运行时行为不同的“对象”。
#include <stdio.h>
struct animal_vtable {
const char* (*sound)();
};
struct animal {
struct animal_vtable methods;
const char* name;
};
const char* cat_sound() {
return "meow!";
}
const char* dog_sound() {
return "bark!";
}
void describe(struct animal *a) {
printf("%s makes \"%s\" sound.\n", a->name, a->methods.sound());
}
struct animal cat = {{&cat_sound}, "cat"};
struct animal dog = {{&dog_sound}, "dog"};
int main() {
describe(&cat);
describe(&dog);
return 0;
}
【讨论】:
struct animal 的成员 methods 成为指针,因此同一“类”的不同实例可以分享他们的 vtables
我猜,你已经查看了polymorphism 上的维基百科文章。
在计算机科学中,多态性是一种编程语言特性 允许使用 a 来处理不同数据类型的值 统一的界面。
根据该定义,不,C 本身并不支持多态性。例如,没有获取数字绝对值的通用函数(abs 和 fabs 分别用于整数和双精度数)。
如果您也熟悉 C++,请查看 OOP 继承和模板 - 这些是那里的多态机制。
【讨论】:
tgmath.h 的标头中添加了算术类型的多态数学函数; C1x 新增_Generic 让用户自定义多态宏成为可能
C 中没有对多态性的内在支持,但是有一些设计模式,使用函数指针、基类“类”(结构)强制转换等,可以提供动态分派的逻辑等价物。 GTK library 就是很好的例子。
【讨论】: