当前位置:  开发笔记 > 运维 > 正文

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

如何解决《如何在C中模拟OO风格的多态?》经验,为你挑选了5个好方法。

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


也可以看看:

你能用C编写面向对象的代码吗?

C中的面向对象

通过搜索"[c] oo"找到.



1> UncleZeiv..:

第一个C++编译器("C with classes")实际上会生成C代码,所以这绝对可行.

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

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)这样的语法变得可能,但你仍然必须显式地将指向结构的指针传递给函数本身.


这个答案是不正确的.将`struct derived*`传递给`function_on_base`将无法编译; `struct derived*`是一个不同于`struct base*`的类型,即使地址是正确的; 但是,如果你将指针从`derived*`转换为`base*`,它将起作用(但你会错过编译时类型检查,而是在运行时崩溃).@PatrickCollins在C中覆盖_is_:http://pastebin.com/W5xEytbv
这没有解释方法重写如何在C中工作.如何在C++多态调用中覆盖function_on_base来访问派生的memeber_y?

2> Peter Štibra..:

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

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

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

每个注册的类型的文件系统的随后登记其自己的功能create,lookup和其余功能.其余代码可以使用泛型inode_operations:

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


听起来很有趣 :-)

3> thinkbeforec..:

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



4> 小智..:

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

#include 

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

我希望能回答一些问题.


好例子.如果你有两个不同的init/get/set的"派生"类会更好."私人"成员/功能可以使用[不透明结构](http://stackoverflow.com/questions/3965279/opaque-c-structs-how-should-they-be-declared)完成.命名约定也很重要:`mylib_someClass_aMethod(this)`是一个很好的可能性.

5> Sébastien Ro..:

VPRI的Ian Piumarta和Alessandro Warth 的文章开放可重用对象模型的附录B是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); \ 
}) 

在同一个文档,有一个看看object,vtable,vtable_delegatedsymbol结构,以及_bindvtable_lookup功能.

干杯!

推荐阅读
mobiledu2402851323
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有