mep*_*ell 353
是.实际上,Axel Schreiner免费提供了他的书 "ANSI-C中的面向对象编程",它非常全面地介绍了这一主题.
pax*_*blo 332
既然你在谈论多态性,那么是的,你可以,我们在C++出现之前的几年就做了那样的事情.
基本上,您使用a struct来保存数据和函数指针列表,以指向该数据的相关函数.
因此,在通信类中,您将进行打开,读取,写入和关闭调用,该调用将作为结构中的四个函数指针以及对象的数据进行维护,例如:
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;
Run Code Online (Sandbox Code Playgroud)
当然,上面那些代码段实际上是在"构造函数"中,例如rs232Init().
当你从该类"继承"时,只需将指针更改为指向自己的函数即可.每个调用这些函数的人都会通过函数指针来完成它,为你提供多态性:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
Run Code Online (Sandbox Code Playgroud)
有点像手动vtable.
通过将指针设置为NULL,您甚至可以拥有虚拟类 - 行为与C++(运行时的核心转储而不是编译时的错误)略有不同.
这是一段演示它的示例代码.首先是顶级类结构:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
Run Code Online (Sandbox Code Playgroud)
然后我们有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;
}
Run Code Online (Sandbox Code Playgroud)
而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;
}
Run Code Online (Sandbox Code Playgroud)
最后是一个测试程序,以显示它在行动:
// 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;
}
Run Code Online (Sandbox Code Playgroud)
这会产生输出:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
Run Code Online (Sandbox Code Playgroud)
所以你可以看到正在调用不同的函数,具体取决于子类.
nat*_*ose 83
命名空间通常通过以下方式完成:
stack_push(thing *)
Run Code Online (Sandbox Code Playgroud)
代替
stack::push(thing *)
Run Code Online (Sandbox Code Playgroud)
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
Run Code Online (Sandbox Code Playgroud)
成
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
Run Code Online (Sandbox Code Playgroud)
并做:
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
}
Run Code Online (Sandbox Code Playgroud)
我没有做析构函数或删除,但它遵循相同的模式.
this_is_here_as_an_example_only就像一个静态类变量 - 在一个类型的所有实例之间共享.所有方法都是静态的,除了有些人拿这个*
Mir*_*mek 53
我相信除了有用之外,在C中实现OOP是学习 OOP和理解其内部工作的一种很好的方法.许多程序员的经验表明,要有效和自信地使用技术,程序员必须了解最终如何实现基础概念.在C中模拟类,继承和多态就是这样教的.
要回答原始问题,这里有几个资源,教授如何在C中进行OOP:
EmbeddedGurus.com博客文章"基于对象的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获得.
tva*_*son 32
我已经看到它完成了.我不推荐它.C++最初是以这种方式开始的,它将C代码作为中间步骤生成.
基本上你最终要做的是为存储函数引用的所有方法创建一个调度表.派生一个类需要复制这个调度表并替换你想要覆盖的条目,新的"方法"必须调用原始方法才能调用基本方法.最终,你最终重写了C++.
Jas*_*ers 15
动物与狗的简单例子:你镜像C++的vtable机制(主要是反正).您还可以分配分配和实例化(Animal_Alloc,Animal_New),因此我们不会多次调用malloc().我们还必须明确地传递this指针.
如果你要做非虚函数,那就是琐事.您只是不将它们添加到vtable中,静态函数不需要this指针.多重继承通常需要多个vtable来解决歧义.
此外,您应该能够使用setjmp/longjmp进行异常处理.
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);
}
Run Code Online (Sandbox Code Playgroud)
PS.这是在C++编译器上测试的,但它应该很容易使它在C编译器上运行.
RJB*_*RJB 12
这很有趣.我自己一直在思考同样的问题,思考它的好处是这样的:
试图想象如何用非OOP语言实现OOP概念有助于我理解OOp语言的优势(在我的例子中,C++).这有助于我更好地判断是否在给定类型的应用程序中使用C或C++ - 其中一个的好处超过另一个.
在我浏览网页以获取有关此信息和意见时,我找到了一位为嵌入式处理器编写代码的作者,并且只提供了一个C编译器:http: //www.eetimes.com/discussion/other/4024626/Object-Oriented -C-创建基金会-类-部分- 1
在他的案例中,分析和调整普通C中的OOP概念是一种有效的追求.由于试图在C中实现它们导致的性能开销,他似乎愿意牺牲一些OOP概念.
我所接受的教训是,是的,它可以在一定程度上完成,是的,有一些很好的理由来尝试它.
最后,机器处理堆栈指针位,使程序计数器跳转并计算内存访问操作.从效率的角度来看,你的程序执行的这些计算越少越好......但有时我们必须支付这个税,因此我们可以组织我们的程序,使其最不容易受到人为错误的影响.OOP语言编译器努力优化这两个方面.程序员必须更加谨慎地用C语言实现这些概念.
ben*_*ado 10
您可能会发现查看Apple的Core Foundation API集文档很有帮助.它是纯C API,但许多类型都桥接到Objective-C对象等价物.
您可能还会发现查看Objective-C本身的设计很有帮助.它与C++略有不同,因为对象系统是根据C函数定义的,例如objc_msg_send调用对象上的方法.编译器将方括号语法转换为那些函数调用,因此您不必知道它,但考虑到您的问题,您可能会发现它有助于了解它是如何工作的.
Pat*_*ter 10
有几种技术可以使用.最重要的是如何拆分项目.我们在项目中使用一个接口,该接口在.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
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
所以我将指针显式地指向该模块的每个函数的对象.C++编译器隐式地执行它,而在C中我们明确地写它.
我真的this在我的程序中使用,以确保我的程序不能用C++编译,并且它在我的语法高亮编辑器中具有另一种颜色的优良特性.
可以在一个模块中修改FOO_struct的字段,甚至不需要重新编译另一个模块以使其仍然可用.
有了这种风格,我已经处理了OOP(数据封装)的很大一部分优势.通过使用函数指针,它甚至可以很容易地实现继承,但老实说,它实际上很少有用.
你可以使用函数指针伪造它,事实上,我认为理论上可以将C++程序编译成C.
然而,强制使用语言范式而不是选择使用范式的语言很少有意义.
面向对象的C,可以做到的,我见过这种类型的代码在生产在韩国,这是最可怕的怪物我看到在年(这是像去年(2007年),我看到的代码).所以,是的这是可以做到的,是的人已经做到了,并且仍然这样做,即使在这个时代.但我建议C++或Objective-C,由C出生都是语言,以提供面向对象的不同范式的目的.
如果您确信OOP方法对于您尝试解决的问题更优越,那么您为什么要尝试使用非OOP语言来解决它?看起来你正在使用错误的工具来完成工作.使用C++或其他一些面向对象的C变体语言.
如果您因为开始使用C编写的现有大型项目进行编码而要求,那么您不应该尝试将自己的(或任何其他人的)OOP范例强制插入到项目的基础结构中.遵循项目中已存在的准则.通常,干净的API和隔离的库和模块将大大有助于实现干净的OOP- ish设计.
如果,这一切后,你真的是做OOP C中,读这个(PDF).
是的你可以.在C++或Objective-C出现之前,人们正在编写面向对象的C语言.C++和Objective-C在某些方面都试图采用C中使用的一些OO概念,并将它们形式化为语言的一部分.
这是一个非常简单的程序,它展示了如何制作看起来像/是方法调用的东西(有更好的方法来实现这一点.这只是语言支持概念的证据):
#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;
}
Run Code Online (Sandbox Code Playgroud)
小智 6
要添加的小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;
}
Run Code Online (Sandbox Code Playgroud)
我一直在挖这个一年:
由于GObject系统很难与纯C一起使用,我尝试编写一些不错的宏来简化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;
}
Run Code Online (Sandbox Code Playgroud)
这是我的项目网站(我没有足够的时间来编写en.doc,但是中文文档要好得多).