Object-oriented C (OOC) kit is for those who want to program in an object-oriented manner, but sticks on the good old C as well. OOC implements classes, single and multiple inheritance, exception handling.
#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;
}
最后是一个测试程序,以显示它在行动:
// 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;
}
#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;
}
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
}
使用动物和狗的简单示例:您多次调用C's vtable mechanism (largely anyway). You also separate allocation and instantiation (Animal_Alloc, Animal_New) so we don' t调用malloc() . 我们还必须明确传递 this 指针 .
如果你要做非虚函数,那么's trival. You just don't将它们添加到vtable中,而静态函数不需要 this 指针 . 多重继承通常需要多个vtable来解决歧义 .
30 回答
看起来人们正在尝试用C模拟C风格 . 我的看法是做面向对象的编程C实际上是在进行面向结构的编程 . 但是,您可以实现后期绑定,封装和继承等功能 . 对于继承,您显式定义了一个指向子结构中基本结构的指针,这显然是一种多重继承的形式 . 你还需要确定你的
用
c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
编译 .所以建议坚持纯粹的C风格,而不是试图强制进入C风格 . 这种方式也适用于构建API的非常简洁的方式 .
这个问题的答案是“是的,你可以” .
Object-oriented C (OOC) kit is for those who want to program in an object-oriented manner, but sticks on the good old C as well. OOC implements classes, single and multiple inheritance, exception handling.
Features
•仅使用C宏和函数,不需要语言扩展! (ANSI-C)
•易于阅读的应用程序源代码 . 注意尽可能简化事情 .
•单继承类
•接口和mixin的多重继承(自1.3版本起)
•实现异常(纯C!)
•类的虚函数
•外部工具,便于实现类
有关更多详细信息,请访问http://ooc-coding.sourceforge.net/ .
我参加派对有点晚了,但我想分享一下我对这个主题的经验:这些天我使用嵌入式东西,而我唯一的(可靠的)编译器是C,所以我想应用面向对象在我用C编写的嵌入式项目中的方法
到目前为止我见过的大多数解决方案都使用了大量的类型转换,因此我们失去了类型安全性:如果你犯了错误,编译器将无法帮助你 . 这是完全不可接受的 .
我的要求:
尽可能避免使用类型转换,因此我们不会失去类型安全性;
多态性:我们应该能够使用虚方法,并且类的用户不应该知道某个特定方法是否是虚拟的;
多重继承:我不经常使用它,但有时我真的想要一些类来实现多个接口(或扩展多个超类) .
我在本文中详细解释了我的方法:Object-oriented programming in C;此外,还有一个实用程序可用于自动生成基类和派生类的样板代码 .
在Jim Larson 1996年在Section 312 Programming Lunchtime Seminar发表的演讲中有一个继承使用C的例子:High and Low-Level C .
我建议使用Objective-C,它是C的超集 .
虽然Objective-C已有30年历史,但它允许编写优雅的代码 .
http://en.wikipedia.org/wiki/Objective-C
是 . 事实上,Axel Schreiner免费提供his book "Object-oriented Programming in ANSI-C",其中涵盖了相关主题 .
既然你在谈论多态性,那么是的,你可以,我们在C出现前几年做了那样的事情 .
基本上,您使用
struct
来保存数据和函数指针列表,以指向该数据的相关函数 .因此,在通信类中,您将进行开放,读取,写入和关闭调用,该调用将作为结构中的四个函数指针以及对象的数据维护,例如:
当然,上面的那些代码段实际上是"constructor",例如
rs232Init()
.当你从该类“继承”时,只需将指针更改为指向自己的函数即可 . 每个调用这些函数的人都会通过函数指针来完成它,为你提供多态性:
有点像手动vtable .
您甚至可以通过将指针设置为NULL来实现虚拟类 - 行为与C(运行时的核心转储而不是编译时的错误)略有不同 .
这是一段演示它的示例代码 . 首先是顶级类结构:
然后我们有TCP'子类'的函数:
而HTTP也是:
最后是一个测试程序,以显示它在行动:
这会产生输出:
所以你可以看到正在调用不同的函数,具体取决于子类 .
如果您确信OOP方法对于您要解决的问题更优越,那么您为什么要尝试使用非OOP语言来解决它?看起来你正在使用错误的工具来完成工作 . 使用C或其他一些面向对象的C变体语言 .
如果您因为开始使用C编写的现有大型项目进行编码而要求,则不应该将OOP范例纳入项目的基础结构中 . 遵循项目中已存在的准则 . 通常,干净的API和隔离的库和模块将大大有助于实现干净的OOP-设计 .
毕竟,如果你真的开始做OOP C,请阅读this(PDF) .
看看GObject . 它's meant to be OO in C and one implementation of what you'正在寻找 . 如果您真的想要OO,请使用C或其他一些OOP语言 . 如果你习惯于约定和流程,GObject可能真的很难处理 .
OOP只是一种范例,它使数据比程序中的代码更重要 . OOP不是一种语言 . 因此,就像普通C是一种简单的语言一样,普通C中的OOP也很简单 .
有几种技术可以使用 . 最重要的是如何拆分项目 . 我们在项目中使用一个接口,该接口在.h文件中声明,并在.c文件中实现该对象 . 重要的是,包含.h文件的所有模块只能将对象看作
void *
,而.c文件是唯一知道结构内部的模块 .对于我们将FOO命名为一个类的类似的东西:
在.h文件中
C实现文件就是这样的 .
所以我将指针显式地指向该模块的每个函数的对象 . C编译器隐式地执行它,而在C中我们将它明确地写出来 .
我真的在我的程序中使用
this
,以确保我的程序不能在C中编译,并且它具有在我的语法高亮编辑器中使用另一种颜色的优良属性 .可以在一个模块中修改FOO_struct的字段,甚至不需要重新编译另一个模块以使其仍然可用 .
有了这种风格,我已经处理了OOP(数据封装)的很大一部分优势 . 通过使用函数指针,它甚至可以很容易地实现继承,但老实说,它实际上很少有用 .
这很有趣 . 我自己一直在思考同样的问题,思考它的好处是这样的:
试图想象如何用非OOP语言实现OOP概念有助于我理解OOp语言的优势(在我的例子中,C) . 这有助于我更好地判断是否将C或C用于特定类型的应用程序 - 其中一个的好处超过另一个 .
在我浏览网页以获取有关此信息和意见时,我找到了一位为嵌入式处理器编写代码并且只提供C编译器的作者:http://www.eetimes.com/discussion/other/4024626/Object-Oriented-C-Creating-Foundation-Classes-Part-1
在他的案例中,分析和调整普通C中的OOP概念是一种有效的追求 . 由于尝试在C中实现它们导致的性能开销,他似乎愿意牺牲一些OOP概念 .
我所接受的教训是,是的,它可以在一定程度上完成,是的,有一些很好的理由来尝试它 .
最后,机器处理堆栈指针位,使程序计数器跳转并计算内存访问操作 . 从效率的角度来看,你的计划所做的这些计算越少越好......但有时我们必须支付这个税,这样我们才能以最不容易出现人为错误的方式组织我们的程序 . OOP语言编译器努力优化这两个方面 . 程序员必须更加谨慎地用C语言实现这些概念 .
当然有可能 . 这就是所有GTK+和GNOME所基于的框架GObject .
您可能会发现查看Apple的Core Foundation API集文档很有帮助 . 它是纯C API,但许多类型都桥接到Objective-C对象等价物 .
您可能还会发现查看Objective-C本身的设计很有帮助 . 它与C略有不同,因为对象系统是根据C函数定义的,例如:
objc_msg_send
在对象上调用方法 . 编译器将方括号语法转换为那些函数调用,因此您不必知道它,但考虑到您的问题,您可能会发现它有助于了解它是如何工作的 .是的你可以 . 人们在C或Objective-C出现之前编写了面向对象的C语言 . C和Objective-C都部分地尝试采用C中使用的一些OO概念,并将它们形式化为语言的一部分 .
这是一个非常简单的程序,它展示了如何制作看起来像/是方法调用的东西(有更好的方法来实现这一点 . 这只是语言支持概念的证据):
是的,但我从未见过有人试图用C实现任何类型的多态性 .
我相信除了有用之外,在C中实现OOP是一个很好的方法,可以了解它的内部工作原理 . 许多程序员的经验表明,要有效和自信地使用技术,程序员必须了解最终如何实现基础概念 . 在C中模拟类,继承和多态就是这样教的 .
要回答原始问题,这里有几个资源,教授如何在C中进行OOP:
EmbeddedGurus.com博客文章"Object-based programming in C"展示了如何在便携式C中实现类和单继承:http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c/
应用笔记“"C+"-C面向对象编程”展示了如何使用预处理器宏在C中实现类,单继承和后期绑定(多态):http://www.state-machine.com/resources/cplus_3.0_manual.pdf,示例代码可从http://www.state-machine.com/resources/cplus_3.0.zip获得
你可以使用函数指针伪造它,事实上,我认为理论上可以将C程序编译成C.
但是,强制使用语言范式而不是选择使用范例的语言很少有意义 .
当然,它不会像使用语言一样漂亮内置支持 . 我甚至写过“面向对象的汇编程序” .
您可能想要做的一件事是查看X Window的Xt工具包的实现 . 当然它在牙齿中变得越来越长,但是许多使用的结构被设计成在传统的C中以OO方式工作 . 通常这意味着在这里和那里添加额外的间接层并设计相互叠加的结构 .
你可以通过这种方式做很多OO位于C的方式,即使它感觉有点像,OO的概念并没有完全形成于
#include<favorite_OO_Guru.h>
的思想中 . 它们确实构成了当时许多既定的最佳实践 . OO语言和系统只是提炼和放大了当时编程时代精神的一部分 .面向对象的C,可以完成,我已经在韩国看到了这种类型的代码,这是我多年来看到的最恐怖的怪物(这就像去年(2007年)我看过的代码) . 所以是的,它可以做到,是的,人们之前已经做过,甚至在这个时代仍然这样做 . 但我推荐C或Objective-C,它们都是C语言的诞生,目的是提供不同范式的面向对象 .
Dave Hanson的C Interfaces and Implementations在封装和命名方面非常出色,而且在使用函数指针方面非常出色 . 戴夫不试图模拟继承 .
我 Build 了一个小图书馆,在那里我尝试过,对我来说它很有效 . 所以我想我分享经验 .
https://github.com/thomasfuhringer/oxygen
单继承可以使用结构很容易地实现,并为每个其他子类扩展它 . 对父结构的简单转换使得可以在所有后代上使用父方法 . 只要您知道变量指向包含此类对象的结构,您就可以始终转换为根类并进行内省 .
如前所述,虚拟方法有点棘手 . 但他们是可行的 . 为了简单起见,我只使用类描述结构中的一系列函数,每个子类复制并在需要时重新填充各个插槽 .
多重继承实现起来相当复杂,并且会产生显着的性能影响 . 所以我离开了 . 我认为在很多情况下干净地模拟现实生活环境是可取和有用的,但在90%的情况下,单一继承可以满足需求 . 单一继承很简单,没有任何成本 .
我也不关心类型安全 . 我认为你不应该依赖编译器来防止编程错误 . 而且无论如何它只会掩盖一小部分错误 .
通常,在面向对象的环境中,您还希望实现引用计数,以尽可能自动化内存管理 . 所以我还将引用计数放入“对象”根类和一些封装堆内存分配和释放的功能 .
这一切都非常简单和精益,给了我OO的基本要素,而不是强迫我处理C的怪物 . 而且我保留了留在C land的灵活性,除此之外,它还可以更容易地集成第三方库 .
命名空间通常通过以下方式完成:
代替
要将C结构化为C++类,您可以转向:
成
并做:
我没有做析构函数或删除,但它遵循相同的模式 .
this_is_here_as_an_example_only就像一个静态类变量 - 在一个类型的所有实例之间共享 . 所有方法都是静态的,除了有些人拿这个*
要添加的小OOC代码:
我见过它 . 我不推荐它 . C最初以这种方式启动,作为一个预处理器,生成C代码作为中间步骤 .
基本上你最终要做的是为存储函数引用的所有方法创建一个调度表 . 派生一个类需要复制这个调度表并替换你想要覆盖的条目,新的“方法”必须调用原始方法,如果它想要调用基本方法 . 最终,你最终重写了C .
请参阅http://slkpg.byethost7.com/instance.html,了解C中OOP的另一个转折 . 它强调使用本机C进行重入的实例数据 . 多重继承是使用函数包装器手动完成的 . 保持类型安全 . 这是一个小样本:
C stdio FILE子库是如何在纯粹的C中创建抽象,封装和模块化的一个很好的例子 .
继承和多态 - 通常被认为对OOP至关重要的其他方面 - 并不一定能提供他们所承诺的 生产环境 力增益,而且他们实际上可以阻碍开发和思考问题 . 域 .
使用动物和狗的简单示例:您多次调用C's vtable mechanism (largely anyway). You also separate allocation and instantiation (Animal_Alloc, Animal_New) so we don' t调用malloc() . 我们还必须明确传递
this
指针 .如果你要做非虚函数,那么's trival. You just don't将它们添加到vtable中,而静态函数不需要
this
指针 . 多重继承通常需要多个vtable来解决歧义 .此外,您应该能够使用setjmp / longjmp进行异常处理 .
PS . 这是在C编译器上测试的,但它应该很容易使它在C编译器上工作 .
我一直在挖这个一年:
由于GObject系统很难与纯C一起使用,我尝试编写一些不错的宏来简化C语言的OO风格 .
这是我的项目网站(我没有足够的时间来编写en.doc,但是中文文档要好得多) .
OOC-GCC