【问题标题】:Virtual functions in C [duplicate]C中的虚函数[重复]
【发布时间】:2011-07-27 12:43:26
【问题描述】:

可能重复:
How could one implement C++ virtual functions in C

在 C++ 中,类和结构之间的唯一区别是默认访问级别。所以你可以在结构中拥有虚函数,从结构继承等等。我的问题是,你也可以在 C 中做到这一点吗?

【问题讨论】:

  • 我知道它们是如何在 C++ 中实现的,只是想知道它是否在 C 中工作。我不知道 virtual 不是 C 中的关键字,所有谷歌搜索都让我找到了 C++ 的东西......
  • 这不是“唯一的区别”,它们的默认继承也不同。 C 不支持任何类型的成员函数 - 虚拟或其他。
  • 简而言之,您的问题没有多大意义,C struct 没有成员函数。 C没有苹果,问有没有青苹果也没用。
  • @Luchian:欢迎来到 SO。如果这个问题已经被关闭,请不要感到沮丧,因为它之前已经被问过(至少两次)。请参阅 Peter 提供的链接问题。

标签: c


【解决方案1】:

您可以使用存储在结构中的函数指针来执行“虚拟函数”。对于继承,您可以将一个结构嵌入到另一个结构中,但语法再次与您所期望的不同。你可以用 C 编写面向对象的程序(经典的例子是 unix file/socket/... API),但是语法很尴尬。

这里的相关答案:https://stackoverflow.com/search?q=C+virtual+functions

【讨论】:

  • 确实——Linux 内核经常这样做。包含多个函数指针的struct(即interface)。每个实现都可以选择只分配一些函数指针;其余为 NULL。
  • 我知道,只是想知道你是否可以在 C 中使用 virtual。
  • @Luchian - 正如其他人所说,C 中不存在 virtual 关键字和相关的编译器限制。但是您可以在 yi_H 和我自己描述的 C 中实现类似的行为。
【解决方案2】:

C 没有虚拟方法的本机语法。但是,您仍然可以通过模仿 C++ 实现虚拟方法的方式来实现虚拟方法。 C++ 为每个虚拟方法在每个类中存储一个指向函数定义的附加指针。因此,您可以简单地将函数指针添加到结构以模拟虚拟方法。

例如

#include <stdio.h>
#include <stdlib.h>

int f2(int x)
{
    printf("%d\n",x);
}

typedef struct mystruct
{
    int (*f)(int);
} mystruct;


int main()
{
    mystruct *s=malloc(sizeof(mystruct));
    s->f=f2;
    s->f(42);
    free(s);
    return 0;
}

【讨论】:

  • +1 不错的尝试。但是你不能在结构定义本身中做int (*f)(int x)=NULL;
  • 我同意 Nawaz - 这必须在实例的定义中(或依赖编译器留下未初始化的字段 0)。此外,在调用它之前,您需要确保 s.f 不是 NULL
  • @Eli 和 Nawaz:感谢您指出这一点。我更新了代码,现在可以用 gcc 编译了。
【解决方案3】:

不,你不能。 'virtual' 不是 C 词汇表的一部分,'access level' 也不是

【讨论】:

  • 确实 virtual 关键字不是 C 语言的一部分。这并不意味着在 C 中不可能进行面向对象编程。
  • -1:缺少virtual 保留字并不意味着您不能拥有虚拟成员函数。根据这个定义,smalltalk 也没有多态性。
  • 不能在 C 中拥有成员函数,不管是不是虚拟的。时期。指向函数的指针是指向函数not 成员函数的指针。请救救我。
  • @hexa:重点是:声明关键字virtual缺少缺少成员函数等的原因人。无效。语言的语法不需要将virtual 定义为关键字来定义运行时多态性或成员函数。同样,语法中缺少这种产生式或原子本身并不表明没有这种功能和行为。因此不是答案。因此-1。期间。
  • 我修剪了一些多余的空白,可以删除我的反对票,我击败了系统:)
【解决方案4】:

您可以使用函数指针模拟虚函数。例如,

struct foo
{
    void(*bar)(struct foo*, int, int);
};

void default_bar ( struct foo * f, int a, int b )
{
    printf("bar(%d,%d)\n", a, b);
}

void setup_foo ( struct foo * f )
{
    f->bar = &default_bar;
}

然后,您可以将结构“子类化”为:

struct meh
{
   /* inherit from "class foo". MUST be first. */
   struct foo base;
   int more_data;
};

/* override "method bar". */
struct custom_bar ( struct foo * f, int a, int b )
{
    struct meh * m = (struct meh*)f;
    printf("custom_bar(%d,%d)\n", a, b);
}

void setup_meh ( struct meh * m )
{
    setup_foo(&m->base);
    m->bar = &custom_bar;
}

所有这些都是劳动密集型且容易出错的,但可以做到。这种类型的“继承”和“覆盖”实现在一些著名的 C 库中很常见,包括jpegliblibpng。如果您对标准 C I/O 不满意,他们使用此技术允许您覆盖 I/O 过程。

编辑:如 cmets 中所述,其中一些代码依赖(官方)非标准行为,“恰好”在大多数编译器上工作。主要问题是代码假定&amp;m.base == &amp;m(例如base 成员的偏移量为0)。如果不是这种情况,则custom_bar() 中的强制转换会导致未定义的行为。要解决此问题,您可以在 struct foo 中添加一个额外的指针,如下所示:

struct foo
{
    /* same as before ...*/
    /* extra pointer. */
    void * hook;
};

然后,修改触及演员的东西,

void setup_meh ( struct meh * m )
{
    m->base.hook = m;
   /* set up function pointers as usual... */
}

void custom_bar ( struct foo * f, int a, int b )
{
    struct meh * m = (struct meh*)f->hook;
    /* override. */
}

这种技术更可靠,特别是如果您打算用 C++ 编写“派生结构”并使用虚函数。在这种情况下,第一个成员的偏移量通常为非 0,因为编译器会在其中存储运行时类型信息和类的 v-table。

【讨论】:

  • 尽管这可行,但您仍依赖编译器扩展来进行类型双关语,这可能导致未定义的行为 blablabla。事实上,这在 GCC 和 MSVC 中有效。 void(*bar)(int, int); 也应该是 void(*bar)(void *, int, int);
  • 谢谢,确实函数指针被声明错误。事实上,对齐的东西依赖于编译器扩展。您可以使用指向“派生对象”的struct foo 中的额外void* 指针使其可移植。然而,该技术在众所周知的库中非常流行,被认为是“便携的”。任何使这种类型的代码中断的编译器都会受到客户的大量投诉。
  • @hexa:删除了未定义的行为。查看编辑。
  • 干得好,如果可以的话,我会再次投票;)
【解决方案5】:

你不能。 C 结构不能有行为。他们只能有数据。

请看http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=tenBestQuestions 了解 C 结构和 C++ 结构之间的差异。 写在第二个问题里。

C++ 结构与 C++ 类与 C 结构不同。这只是一个类比。

另外,C 中没有继承之类的东西。没有继承,你会用虚函数做什么?

【讨论】:

  • 问题:是函数指针数据还是“行为”?
  • 我对函数指针了解不多,但我认为它是一个数据。毕竟它是一个指针不是吗?
  • 您可以通过将“base”结构作为“child”结构的第一个成员来在 C 中进行继承。然后在实例化“子”结构后,只需将其函数指针设置为被覆盖的方法。
猜你喜欢
  • 2013-07-30
  • 2012-08-27
  • 2021-12-30
  • 1970-01-01
  • 2021-01-26
  • 2016-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多