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

你能用C编写面向对象的代码吗? 特别是在多态性方面。


请参阅堆栈溢出问题C中的面向对象

是。 实际上Axel Schreiner免费提供了他的书 “面向对象的ANSI-C编程”,其内容非常透彻。

既然你在谈论多态,那么是的,你可以,我们在C ++出现之前做了那么多年。

基本上你使用一个struct来保存数据和一个函数指针列表来指向数据的相关函数。

所以,在一个通信类中,你可以进行一个open,read,write和close的调用,这个调用将在结构中作为四个函数指针维护,同时还有一个对象的数据,如下所示:

 typedef struct { int (*open)(void *self, char *fspec); int (*close)(void *self); int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz); int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz); // And data goes here. } tCommClass; tCommClass commRs232; commRs232.open = &rs232Open; : : commRs232.write = &rs232Write; tCommClass commTcp; commTcp.open = &tcpOpen; : : commTcp.write = &tcpWrite; 

当然,上面那些代码段实际上是在“构造函数”,如rs232Init()

当你从这个类“inheritance”时,你只需要改变指向你自己函数的指针即可。 每个调用这些函数的人都可以通过函数指针来实现,给你多态:

 int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000"); 

有点像手动vtable。

甚至可以通过将指针设置为NULL来实现虚拟类 – 行为与C ++(运行时的核心转储,而不是编译时的错误)略有不同。

下面是一段演示它的示例代码。 首先是顶级的class级结构:

 #include <stdio.h> // The top-level class. typedef struct sCommClass { int (*open)(struct sCommClass *self, char *fspec); } tCommClass; 

然后我们有了TCP'子类'的函数:

 // Function for the TCP 'class'. static int tcpOpen (tCommClass *tcp, char *fspec) { printf ("Opening TCP: %s\n", fspec); return 0; } static int tcpInit (tCommClass *tcp) { tcp->open = &tcpOpen; return 0; } 

和HTTP一样:

 // Function for the HTTP 'class'. static int httpOpen (tCommClass *http, char *fspec) { printf ("Opening HTTP: %s\n", fspec); return 0; } static int httpInit (tCommClass *http) { http->open = &httpOpen; return 0; } 

最后是一个testing程序来展示它的行动:

 // Test program. int main (void) { int status; tCommClass commTcp, commHttp; // Same 'base' class but initialised to different sub-classes. tcpInit (&commTcp); httpInit (&commHttp); // Called in exactly the same manner. status = (commTcp.open)(&commTcp, "bigiron.box.com:5000"); status = (commHttp.open)(&commHttp, "http://www.microsoft.com"); return 0; } 

这产生输出:

 Opening TCP: bigiron.box.com:5000 Opening HTTP: http://www.microsoft.com 

所以你可以看到不同的函数被调用,这取决于子类。

命名空间通常是通过做:

 stack_push(thing *) 

代替

 stack::push(thing *) 

为了将C结构变成类似C ++类的东西,你可以打开:

 class stack { public: stack(); void push(thing *); thing * pop(); static int this_is_here_as_an_example_only; private: ... }; 

 struct stack { struct stack_type * my_type; // Put the stuff that you put after private: here }; struct stack_type { void (* construct)(struct stack * this); // This takes uninitialized memory struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it void (*push)(struct stack * this, thing * t); // Pushing t onto this stack thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it int this_is_here_as_an_example_only; }Stack = { .construct = stack_construct, .operator_new = stack_operator_new, .push = stack_push, .pop = stack_pop }; // All of these functions are assumed to be defined somewhere else 

并做:

 struct stack * st = Stack.operator_new(); // Make a new stack if (!st) { // Do something about it } else { // You can use the stack stack_push(st, thing0); // This is a non-virtual call Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push st->my_type.push(st, thing2); // This is a virtual call } 

我没有做析构或删除,但它遵循相同的模式。

this_is_here_as_an_example_only就像一个静态类variables – 在一个types的所有实例中共享。 所有的方法都是静态的,除了有些采取这个*

我相信,除了自己有用之外,在C中实现OOP是学习 OOP并理解其内部工作的一个很好的方法。 许多程序员的经验表明,为了高效而自信地使用技术,程序员必须了解底层概念如何最终实现。 在C中模拟类,inheritance和多态性教导了这一点。

为了回答最初的问题,下面是一些教会如何在C中执行OOP的资源:

EmbeddedGurus.com博客文章“基于对象的C编程”展示了如何在C中实现类和单inheritance: http : //embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /

应用笔记“”C +“ – 面向对象编程”展示了如何使用预处理macros在C中实现类,单inheritance和后期绑定(多态): http : //www.state-machine.com/resources/cplus_3。 0_manual.pdf ,示例代码可从http://www.state-machine.com/resources/cplus_3.0.zip

我已经看到了。 我不会推荐它。 C ++最初是以一种预处理器的forms开始的,该预处理器生成C代码作为中间步骤。

基本上你最终要做的是为所有存储函数引用的方法创build一个调度表。 如果派生类需要调用基方法,那么派生类将需要复制这个派发表,并用新的“方法”必须调用原始方法来replace要覆盖的条目。 最终,你最终重写C ++。

当然,这是可能的。 这就是GObject , GTK +和GNOME所有基于的框架所做的。

C stdio FILE子库是一个非常好的例子,说明如何在不掺杂的C中创build抽象,封装和模块化。

inheritance和多态 – 其他方面通常被认为是面向对象必不可less的 – 并不一定能提供他们承诺的生产力收益,并且已经提出了合理的 论据 ,认为它们实际上可能阻碍了对问题领域的发展和思考。

动物和狗的简单例子:你反映了C ++的vtable机制(很大程度上是反正)。 你也分开分配和实例化(Animal_Alloc,Animal_New),所以我们不要多次调用malloc()。 我们还必须明确地传递this指针。

如果你要做非虚拟的function,这是trival。 你只是不要把它们添加到vtable中,静态函数不需要this指针。 多重inheritance通常需要多个vtable来解决歧义。

另外,你应该可以使用setjmp / longjmp来做exception处理。

 struct Animal_Vtable{ typedef void (*Walk_Fun)(struct Animal *a_This); typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This); Walk_Fun Walk; Dtor_Fun Dtor; }; struct Animal{ Animal_Vtable vtable; char *Name; }; struct Dog{ Animal_Vtable vtable; char *Name; // Mirror member variables for easy access char *Type; }; void Animal_Walk(struct Animal *a_This){ printf("Animal (%s) walking\n", a_This->Name); } struct Animal* Animal_Dtor(struct Animal *a_This){ printf("animal::dtor\n"); return a_This; } Animal *Animal_Alloc(){ return (Animal*)malloc(sizeof(Animal)); } Animal *Animal_New(Animal *a_Animal){ a_Animal->vtable.Walk = Animal_Walk; a_Animal->vtable.Dtor = Animal_Dtor; a_Animal->Name = "Anonymous"; return a_Animal; } void Animal_Free(Animal *a_This){ a_This->vtable.Dtor(a_This); free(a_This); } void Dog_Walk(struct Dog *a_This){ printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name); } Dog* Dog_Dtor(struct Dog *a_This){ // Explicit call to parent destructor Animal_Dtor((Animal*)a_This); printf("dog::dtor\n"); return a_This; } Dog *Dog_Alloc(){ return (Dog*)malloc(sizeof(Dog)); } Dog *Dog_New(Dog *a_Dog){ // Explict call to parent constructor Animal_New((Animal*)a_Dog); a_Dog->Type = "Dog type"; a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk; a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor; return a_Dog; } int main(int argc, char **argv){ /* Base class: Animal *a_Animal = Animal_New(Animal_Alloc()); */ Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc()); a_Animal->vtable.Walk(a_Animal); Animal_Free(a_Animal); } 

PS。 这是在C ++编译器上testing的,但应该很容易使它在C编译器上工作。

这是有趣的阅读。 我一直在思考同样的问题,思考这个问题的好处是:

  • 试图设想如何在非OOP语言中实现OOP概念有助于我理解OOp语言(就我而言,C ++)的优势。 这有助于我更好地判断是否使用C或C ++作为给定types的应用程序 – 其中一个应用程序的好处在于另一个应用程序的好处。

  • 在我浏览Web的信息和意见,我发现一个作家正在编写一个embedded式处理器的代码,只有一个C编译器可用: http : //www.eetimes.com/discussion/other/4024626/Object-Oriented -C-创build基金会-类-部分- 1

在他的情况下,用C语言分析和调整面向对象的概念是一个有效的追求。 由于尝试在C语言中实现它们而导致的性能开销,他似乎愿意牺牲一些OOP概念。

我所采取的教训是,可以做到一定程度,是的,有很好的理由去尝试。

最后,机器正在debugging堆栈指针位,使程序计数器跳转并计算内存访问操作。 从效率的angular度来看,您的程序所做的这些计算越less越好……但是有时候我们必须支付这笔税款,所以我们可以组织我们的程序,使其最不易受人为错误的影响。 OOP语言编译器力求优化这两个方面。 程序员必须更加小心地用C语言来实现这些概念。

看看GObject 。 这意味着在C中的OO和一个你正在寻找的实现。 如果你真的想要面向对象,请使用C ++或其他一些OOP语言。 如果你习惯于处理面向对象的语言,那么GObject可能非常难以处理,但是就像任何事情一样,你将习惯于惯例和stream程。

您可能会发现查看其Core Foundation API集的Apple文档很有帮助。 它是一个纯粹的C API,但是许多types被桥接到Objective-C对象。

Objective-C本身的devise也可能对您有所帮助。 与C ++有点不同,对象系统是用C函数定义的,例如objc_msg_send来调用对象的方法。 编译器将方括号的语法翻译成那些函数调用,所以你不必知道它,但考虑到你的问题,你可能会发现它是如何工作的。

有几种技术可以使用。 最重要的是如何拆分项目。 我们在我们的项目中使用了一个在.h文件中声明的接口,并且在.c文件中实现了对象。 最重要的部分是包含.h文件的所有模块只将对象视为void * ,而.c文件是知道结构内部的唯一模块。

像这样的一个类,我们把FOO命名为一个例子:

在.h文件中

 #ifndef FOO_H_ #define FOO_H_ ... typedef struct FOO_type FOO_type; /* That's all the rest of the program knows about FOO */ /* Declaration of accessors, functions */ FOO_type *FOO_new(void); void FOO_free(FOO_type *this); ... void FOO_dosomething(FOO_type *this, param ...): char *FOO_getName(FOO_type *this, etc); #endif 

C实现文件将是这样的。

 #include <stdlib.h> ... #include "FOO.h" struct FOO_type { whatever... }; FOO_type *FOO_new(void) { FOO_type *this = calloc(1, sizeof (FOO_type)); ... FOO_dosomething(this, ); return this; } 

所以我把指针明确地指向一个对象到该模块的每个函数。 一个C ++编译器会隐式执行它,而在C中,我们将它明确地写出来。

我真的在我的程序中使用this ,以确保我的程序不会在C ++中编译,并且在我的语法高亮编辑器中具有另一种颜色的优良属性。

FOO_struct的字段可以在一个模块中修改,另一个模块甚至不需要重新编译为可用。

用这种风格,我已经处理了OOP(数据封装)的很大一部分优点。 通过使用函数指针,甚至可以很容易地实现类似inheritance的东西,但是说实话,它实际上只是很less用到。

面向对象的C可以完成,我已经看到了在韩国生产的那种types的代码,这是我多年以来看到的最可怕的怪物(就像去年(2007年),我看到了代码)。 所以是的,这是可以做到的,是的,人们之前做过,甚至在这个时代仍然做。 但是我推荐C ++或者Objective-C,它们都是C语言的语言,目的是为不同的范例提供面向对象的方法。

如果您确信面向对象的方法对于您要解决的问题是优越的,那么为什么要用非面向对象的语言来解决呢? 看起来你正在使用错误的工具来完成这项工作。 使用C ++或其他一些面向对象的C变体语言。

如果你问的是因为你已经开始编写一个已经存在的用C编写的大型项目,那么你不应该试图强迫你自己的(或其他任何人)OOP范例进入项目的基础设施。 遵循项目中已经存在的指导原则。 一般来说,干净的API和独立的库和模块将会带来一个干净的OOPdevise。

如果毕竟这样,你真的开始做OOP C了,请阅读(PDF)。

是的你可以。 在C ++或Objective-C出现之前,人们正在编写面向对象的C语言。 C ++和Objective-C都是部分地尝试去使用C语言中的一些OO概念,并将它们forms化为语言的一部分。

下面是一个非常简单的程序,它展示了如何创build一个看起来像/是方法调用的东西(有更好的方法可以做到这一点,这只是certificate语言支持这些概念):

 #include<stdio.h> struct foobarbaz{ int one; int two; int three; int (*exampleMethod)(int, int); }; int addTwoNumbers(int a, int b){ return a+b; } int main() { // Define the function pointer int (*pointerToFunction)(int, int) = addTwoNumbers; // Let's make sure we can call the pointer int test = (*pointerToFunction)(12,12); printf ("test: %u \n", test); // Now, define an instance of our struct // and add some default values. struct foobarbaz fbb; fbb.one = 1; fbb.two = 2; fbb.three = 3; // Now add a "method" fbb.exampleMethod = addTwoNumbers; // Try calling the method int test2 = fbb.exampleMethod(13,36); printf ("test2: %u \n", test2); printf("\nDone\n"); return 0; } 

当然,它不会像使用内置支持的语言那么漂亮。 我甚至写过“面向对象的汇编器”。

一点OOC代码来添加:

 #include <stdio.h> struct Node { int somevar; }; void print() { printf("Hello from an object-oriented C method!"); }; struct Tree { struct Node * NIL; void (*FPprint)(void); struct Node *root; struct Node NIL_t; } TreeA = {&TreeA.NIL_t,print}; int main() { struct Tree TreeB; TreeB = TreeA; TreeB.FPprint(); return 0; } 

你可以使用函数指针来伪造它,事实上,我认为理论上可以将C ++程序编译为C.

然而,强制一种语言的范式,而不是select一种使用范式的语言是很有意义的。

吉姆·拉尔森(Jim Larson)在1996年的第三 百二十二期课程编程午餐时间研讨会上发表的讲话中有一个使用C的例子: 高级和低级C。

C中哪些文章或书籍可以使用OOP概念?

Dave Hanson的C接口和实现在封装和命名方面非常出色,并且非常适合使用函数指针。 戴夫不试图模拟inheritance。

面向对象只是一种将数据放在程序中比代码更重要的范例。 OOP不是一种语言。 所以,就像纯C是一个简单的语言一样,纯C中的OOP也是简单的。

您可能想要做的一件事就是查看X Window工具包的实现。 当然,它在牙齿上变得很长,但是许多使用的结构被devise成在传统的C中以OO方式工作。通常这意味着在这里和那里增加额外的间接层,并且devise结构以相互叠置。

用这种方式,你可以真正地做很多事情,尽pipe它有时候会有这种感觉,OO的概念并没有从#include<favorite_OO_Guru.h>的思想中完全形成。 他们确实构成了当时已确立的许多最佳做法。 面向对象的语言和系统只是蒸馏和放大了当今编程时代精神的一部分。

我一直在挖这一年:

由于GObject系统很难与纯C一起使用,所以我试图编写一些漂亮的macros来缓解C的OO风格。

 #include "OOStd.h" CLASS(Animal) { char *name; STATIC(Animal); vFn talk; }; static int Animal_load(Animal *THIS,void *name) { THIS->name = name; return 0; } ASM(Animal, Animal_load, NULL, NULL, NULL) CLASS_EX(Cat,Animal) { STATIC_EX(Cat, Animal); }; static void Meow(Animal *THIS){ printf("Meow!My name is %s!\n", THIS->name); } static int Cat_loadSt(StAnimal *THIS, void *PARAM){ THIS->talk = (void *)Meow; return 0; } ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL) CLASS_EX(Dog,Animal){ STATIC_EX(Dog, Animal); }; static void Woof(Animal *THIS){ printf("Woof!My name is %s!\n", THIS->name); } static int Dog_loadSt(StAnimal *THIS, void *PARAM) { THIS->talk = (void *)Woof; return 0; } ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL) int main(){ Animal *animals[4000]; StAnimal *f; int i = 0; for (i=0; i<4000; i++) { if(i%2==0) animals[i] = NEW(Dog,"Jack"); else animals[i] = NEW(Cat,"Lily"); }; f = ST(animals[0]); for(i=0; i<4000; ++i) { f->talk(animals[i]); } for (i=0; i<4000; ++i) { DELETE0(animals[i]); } return 0; } 

这里是我的项目网站(我没有足够的时间来写en doc,但是中文的doc好多了)。

OOC-GCC

这个问题的答案是“是的,你可以”。

面向对象的C(OOC)套件面向那些想要以面向对象的方式进行编程的人,但是也坚持使用老的C语言。 OOC实现类,单个和多个inheritance,exception处理。

特征

•仅使用Cmacros和函数,不需要任何语言扩展! (ANSI-C)

•易于阅读的应用程序源代码。 小心使事情尽可能简单。

•类的单一inheritance

•通过接口和mixins进行多重inheritance(从版本1.3开始)

•实现exception(纯C!)

•类的虚拟function

•易于上课的外部工具

有关更多详细信息,请访问http://ooc-coding.sourceforge.net/

这似乎是人们试图模仿使用C的C ++风格。我的意思是做面向对象的编程C实际上是在做面向结构的编程。 但是,您可以实现后期绑定,封装和inheritance等function。 对于inheritance,你明确地定义了一个指向你的子结构中的基础结构的指针,这显然是一个多重inheritance的forms。 你还需要确定你的

 //private_class.h struct private_class; extern struct private_class * new_private_class(); extern int ret_a_value(struct private_class *, int a, int b); extern void delete_private_class(struct private_class *); void (*late_bind_function)(struct private_class *p); //private_class.c struct inherited_class_1; struct inherited_class_2; struct private_class { int a; int b; struct inherited_class_1 *p1; struct inherited_class_2 *p2; }; struct inherited_class_1 * new_inherited_class_1(); struct inherited_class_2 * new_inherited_class_2(); struct private_class * new_private_class() { struct private_class *p; p = (struct private_class*) malloc(sizeof(struct private_class)); p->a = 0; p->b = 0; p->p1 = new_inherited_class_1(); p->p2 = new_inherited_class_2(); return p; } int ret_a_value(struct private_class *p, int a, int b) { return p->a + p->b + a + b; } void delete_private_class(struct private_class *p) { //release any resources //call delete methods for inherited classes free(p); } //main.c struct private_class *p; p = new_private_class(); late_bind_function = &implementation_function; delete_private_class(p); 

c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj编译c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj

所以build议是坚持一个纯粹的C风格,而不是试图强制成C ++风格。 这种方式也适合于构buildAPI的非常干净的方式。

请参见http://slkpg.byethost7.com/instance.html,以获得另一个关于C的OOP。它强调只使用本地C重入实例数据。使用函数包装器手动完成多重inheritance。; types安全得到维护。 这是一个小样本:

 typedef struct _peeker { log_t *log; symbols_t *sym; scanner_t scan; // inherited instance peek_t pk; int trace; void (*push) ( SELF *d, symbol_t *symbol ); short (*peek) ( SELF *d, int level ); short (*get) ( SELF *d ); int (*get_line_number) ( SELF *d ); } peeker_t, SlkToken; #define push(self,a) (*self).push(self, a) #define peek(self,a) (*self).peek(self, a) #define get(self) (*self).get(self) #define get_line_number(self) (*self).get_line_number(self) INSTANCE_METHOD int (get_line_number) ( peeker_t *d ) { return d->scan.line_number; } PUBLIC void InitializePeeker ( peeker_t *peeker, int trace, symbols_t *symbols, log_t *log, list_t *list ) { InitializeScanner ( &peeker->scan, trace, symbols, log, list ); peeker->log = log; peeker->sym = symbols; peeker->pk.current = peeker->pk.buffer; peeker->pk.count = 0; peeker->trace = trace; peeker->get_line_number = get_line_number; peeker->push = push; peeker->get = get; peeker->peek = peek; } 

我对这个聚会有点晚了,但我想分享一下我的经验:我现在使用embedded式的东西,而我拥有的唯一(可靠的)编译器是C,所以我想要应用面向对象在我用C编写的embedded式项目中

到目前为止,我所看到的大多数解决scheme都大量使用types转换,所以我们失去了types安全性:如果你犯了一个错误,编译器不会帮助你。 这是完全不能接受的。

我有的要求:

  • 尽可能避免types转换,所以我们不会丢失types安全;
  • 多态性:我们应该能够使用虚拟方法,并且类的用户不应该知道某个特定的方法是否是虚拟的;
  • 多重inheritance:我不经常使用它,但有时候我真的希望有一个类实现多个接口(或者扩展多个超类)。

我在本文中详细解释了我的方法: C中的面向对象编程 ; 此外,还有一个用于自动生成基类和派生类的样板代码的实用程序。

我在那里build立了一个小型图书馆,对我来说,它的工作非常好。 所以我想我分享的经验。

https://github.com/thomasfuhringer/oxygen

单一的inheritance可以很容易地使用一个结构体来实现,并且可以对每一个其他的子类进行扩展。 对父结构进行简单的转换可以在所有后代上使用父方法。 As long as you know that a variable points to a struct holding this kind of an object you can always cast to the root class and do introspection.

As has been mentioned, virtual methods are somewhat trickier. But they are doable. To keep things simple I just use an array of functions in the class description structure which every child class copies and repopulates individual slots where required.

Multiple inheritance would be rather complicated to implement and comes with a significant performance impact. So I leave it. I do consider it desirable and useful in quite a few cases to cleanly model real life circumstances, but in probably 90% of cases single inheritance covers the needs. And single inheritance is simple and costs nothing.

Also I do not care about type safety. I think you should not depend on the compiler to prevent you from programming mistakes. And it shields you only from a rather small part of errors anyway.

Typically, in an object oriented environment you also want to implement reference counting to automate memory management to the extent possible. So I also put a reference count into the “Object” root class and some functionality to encapsulate allocation and deallocation of heap memory.

It is all very simple and lean and gives me the essentials of OO without forcing me to deal with the monster that is C++. And I retain the flexibility of staying in C land, which among other things makes it easier to integrate third party libraries.

I propose to use Objective-C, which is a superset of C.

While Objective-C is 30 years old, it allows to write elegant code.

http://en.wikipedia.org/wiki/Objective-C

Yes, but I have never seen anyone attempt to implement any sort of polymorphism with C.