【问题标题】:Polymorphism (in C) [duplicate]多态性(在 C 中)[重复]
【发布时间】:2012-01-01 21:58:03
【问题描述】:

可能重复:
How can I simulate OO-style polymorphism in C?

我试图通过我所知道的语言中的示例来更好地理解多态性的概念; C语言中是否存在多态性?

【问题讨论】:

标签: c oop polymorphism


【解决方案1】:

这是Nekuromento's second example,我认为这是面向对象 C 的惯用方式:

动物.h

#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

cat.c

#include "animal.h"

static const char *sound(void)
{
    return "meow!";
}

const struct animal_vtable_ CAT[] = { { sound } };

dog.c

#include "animal.h"

static const char *sound(void)
{
    return "arf!";
}

const struct animal_vtable_ DOG[] = { { sound } };

main.c

#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 中是否约定俗成?我可以想象他们也可以进入各自的标题。
【解决方案2】:

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;
}

【讨论】:

  • 在 C 中执行 OO 的“惯用”方式(只要有这样的事情)将使 struct animal 的成员 methods 成为指针,因此同一“类”的不同实例可以分享他们的 vtables
  • @Christoph:这是一个额外的间接级别,可能是好事也可能是坏事。这是空间与速度的权衡。
  • @yi_H:当然,但我仍然会考虑使用惯用的指针,因为 vtable 对应于对象的类,它(至少在概念上)在实例之间共享;如果这样看的话,包括实例中的vtable提供了一个方法缓存,即是一种优化的形式……
  • @Christoph:那是因为你在类中思考。想象一个可以独立设置每个 vtable 条目的系统。有一种类方法会导致组合爆炸。再说一次,我只是说没有正确的方法,有取舍。
  • “Woof”是 Dog 发出的惯用噪音,在 RFC K9 中建立
【解决方案3】:

我猜,你已经查看了polymorphism 上的维基百科文章。

在计算机科学中,多态性是一种编程语言特性 允许使用 a 来处理不同数据类型的值 统一的界面。

根据该定义,不,C 本身并不支持多态性。例如,没有获取数字绝对值的通用函数(abs 和 fabs 分别用于整数和双精度数)。

如果您也熟悉 C++,请查看 OOP 继承和模板 - 这些是那里的多态机制。

【讨论】:

  • 您需要将指针转换为指向它的指针,因此从技术上讲,它们不再属于不同类型。不过,这是一种可能的解决方法。
  • C99 在名为 tgmath.h 的标头中添加了算术类型的多态数学函数; C1x 新增_Generic 让用户自定义多态宏成为可能
【解决方案4】:

C 中没有对多态性的内在支持,但是有一些设计模式,使用函数指针、基类“类”(结构)强制转换等,可以提供动态分派的逻辑等价物。 GTK library 就是很好的例子。

【讨论】:

    猜你喜欢
    • 2012-02-19
    • 1970-01-01
    • 2018-01-15
    • 2012-11-21
    • 1970-01-01
    • 2017-01-02
    • 2014-09-11
    • 2015-10-26
    • 2015-05-07
    相关资源
    最近更新 更多