如何在C中模拟OO风格的多态?

时间:2009-02-07 16:13:18

标签: c oop polymorphism

有没有办法用C编程语言编写类似OO的代码?


另见:

通过搜索“[c] oo”找到。

10 个答案:

答案 0 :(得分:59)

第一个C ++编译器(“带有类的C”)实际上会生成C代码,所以这绝对是可行的。

基本上,你的基类是一个结构;派生结构必须在第一个位置包含基本结构,因此指向“派生”结构的指针也将是指向基本结构的有效指针。

typedef struct {
   data member_x;
} base;

typedef struct {
   struct base;
   data member_y;
} derived;

void function_on_base(struct base * a); // here I can pass both pointers to derived and to base

void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class

函数可以作为函数指针的结构的一部分,因此像p-> call(p)这样的语法变得可能,但你仍然必须显式地将指向结构的指针传递给函数本身。

答案 1 :(得分:51)

常用方法是使用指向函数的指针定义struct。这定义了可以在任何类型上调用的“方法”。子类型然后在这个公共结构中设置它们自己的函数,并返回它。

例如,在linux内核中,有struct:

struct inode_operations {
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
    struct dentry * (*lookup) (struct inode *,struct dentry *, 
                               struct nameidata *);
    ...
};

然后,每个注册类型的文件系统都会为createlookup和其余功能注册自己的函数。其余代码可以使用泛型inode_operations:

struct inode_operations   *i_op;
i_op -> create(...);

答案 2 :(得分:27)

C ++离C不远。

类是具有指向名为VTable的函数指针表的隐藏指针的结构。 Vtable本身是静态的。 当类型指向具有相同结构的Vtables但指针指向其他实现时,您将获得多态性。

建议在函数中封装调用逻辑,将struct作为参数,以避免代码混乱。

您还应该在函数中封装结构实例化和初始化(这相当于C ++构造函数)和删除(C ++中的析构函数)。无论如何,这些都是很好的做法。

typedef struct
{
   int (*SomeFunction)(TheClass* this, int i);
   void (*OtherFunction)(TheClass* this, char* c);
} VTable;

typedef struct
{
   VTable* pVTable;
   int member;

} TheClass;

调用方法:

int CallSomeFunction(TheClass* this, int i)
{
  (this->pVTable->SomeFunction)(this, i);
}

答案 3 :(得分:15)

我查看了所有人的答案并提出了这个问题:

#include <stdio.h>

typedef struct
{
    int (*get)(void* this);
    void (*set)(void* this, int i);
    int member;

} TheClass;

int Get(void* this)
{
    TheClass* This = (TheClass*)this;
    return This->member;
}

void Set(void* this, int i)
{
    TheClass* This = (TheClass*)this;
    This->member = i;
}

void init(TheClass* this)
{
    this->get = &Get;
    this->set = &Set;
}

int main(int argc, char **argv)
{
    TheClass name;
    init(&name);
    (name.set)(&name, 10);
    printf("%d\n", (name.get)(&name));
    return 0;
}

我希望能回答一些问题。

答案 4 :(得分:12)

{p <3}}的文章Open Reusable Object Models的附录B,VPRI的Iless Piumarta和Alessandro Warth是GNU C中对象模型的实现,大约140行代码。这是一个引人入胜的读物!

这是使用GNU扩展到C(语句表达式)向对象发送消息的宏的未缓存版本:

struct object;

typedef struct object *oop; 
typedef oop *(*method_t)(oop receiver, ...);

//...

#define send(RCV, MSG, ARGS...) ({ \ 
    oop r = (oop)(RCV); \ 
    method_t method = _bind(r, (MSG)); \ 
    method(r, ##ARGS); \ 
}) 

在同一个文档中,查看objectvtablevtable_delegatedsymbol结构,以及_bind和{{1}函数。

干杯!

答案 5 :(得分:1)

文件函数fopen,fclose,fread是C中OO代码的示例。它们不是类中的私有数据,而是处理用于封装数据的FILE结构,C函数充当成员类函数。 http://www.amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016

答案 6 :(得分:0)

来自维基百科: 在编程语言和类型理论中,多态性(来自希腊语πολύς,polys,“many,much”和μορφή,morphē,“form,shape”)是为不同类型的实体提供单一接口。

所以我想说在C中实现它的唯一方法是使用可变参数和一些(半)自动类型信息管理。 例如,在C ++中你可以写(抱歉琐碎):

void add( int& result, int a1, int a2 );
void add( float& result, float a1, float a2 );
void add( double& result, double a1, double a2 );

在C中,除其他解决方案外,您可以做的最好的事情是:

int int_add( int a1, int a2 );
float float_add( float a1, fload a2 );
double double_add( double a1, double a2 );

void add( int typeinfo, void* result, ... );

然后你需要:

  1. 使用枚举/宏
  2. 实现“typeinfo”
  3. 用stdarg.h stuff实现后一个函数
  4. 告别C静态类型检查
  5. 我几乎可以肯定,任何其他多态的实现应该看起来非常像这样。 相反,上面的答案似乎试图解决继承问题而不是多态性!

答案 7 :(得分:0)

#include <stdio.h>

typedef struct {
    int  x;
    int z;
} base;

typedef struct {
    base;
    int y;
    int x;
} derived;

void function_on_base( base * a) // here I can pass both pointers to derived and to base
{
    printf("Class base [%d]\n",a->x);
    printf("Class base [%d]\n",a->z);
}
void function_on_derived( derived * b) // here I must pass a pointer to the derived class
{
    printf("Class derived [%d]\n",b->y);
    printf("Class derived [%d]\n",b->x);
}

int main()
{
    derived d;
    base b;
    printf("Teste de poliformismo\n");

    b.x = 2;
    d.y = 1;
    b.z = 3;
    d.x = 4;
    function_on_base(&b);
    function_on_base(&d);
    function_on_derived(&b);
    function_on_derived(&d);
    return 0;
}

输出结果为:

Class base [3]
Class base [1]
Class base [4]
Class derived [2]
Class derived [3]
Class derived [1]
Class derived [4]

所以它有效,它是一个多态代码。

UncleZeiv在开始时对此进行了解释。

答案 8 :(得分:0)

为了在C中构建OO功能,您可以查看以前的答案。

但是,如果你想通过C语言中的例子来理解什么是多态,那么(因为在其他问题中已经被重定向到这个问题)。也许我错了,但我想不出像C指针算术一样容易理解的东西。在我看来,指针算法在C 中具有固有的多态性。在下面的示例中,相同的函数(OO中的方法),即加法(+),将根据输入结构的属性产生不同的行为。

示例:

double a*;
char str*;

a=(double*)malloc(2*sizeof(double));
str=(char*)malloc(2*sizeof(char)); 

a=a+2; // make the pointer a, point 2*8 bytes ahead.

str=str+2; // make the pointer str, point 2*1 bytes ahead.

免责声明:我是C的新人,非常期待被纠正并从其他用户的评论中学习,甚至完全抹掉这个答案,如果它是错的。非常感谢,

答案 9 :(得分:0)

我通常想做的是将结构包装在另一个包含有关包装类的元信息的结构中,然后构建作用在通用结构上的类似访问者的函数列表。这种方法的优点是您无需修改​​现有结构,而可以为任​​何结构子集创建访问者。

以通常的示例为例:

typedef struct {
    char call[7] = "MIAO!\n";
} Cat;
    
typedef struct {
    char call[6] = "BAU!\n";
} Dog;

我们可以将2种结构包裹在这个新结构中:

typedef struct {
    const void * animal;
    AnimalType type;
} Animal;

类型可以是一个简单的int,但不要懒惰:

typedef enum  {
    ANIMAL_CAT = 0,
    ANIMAL_DOG,
    ANIMAL_COUNT
} AnimalType;

最好有一些包装功能:

Animal catAsAnimal(const Cat * c) {
    return (Animal){(void *)c, ANIMAL_CAT};
}

Animal dogAsAnimal(const Dog * d) {
    return (Animal){(void *)d, ANIMAL_DOG};
}

现在我们可以定义“访问者”:

void catCall ( Animal a ) {
    Cat * c = (Cat *)a.animal;
    printf(c->call);
}

void dogCall ( Animal a ) {
    Dog * d = (Dog *)a.animal;
    printf(d->call);
}

void (*animalCalls[ANIMAL_COUNT])(Animal)={&catCall, &dogCall};

那么实际用法将是:

Cat cat;
Dog dog;

Animal animals[2];
animals[0] = catAsAnimal(&cat);
animals[1] = dogAsAnimal(&dog);

for (int i = 0; i < 2; i++) {
    Animal a = animals[i];
    animalCalls[a.type](a);
}

此方法的缺点是,每次要将其用作泛型类型时,都必须包装结构。