Ben*_*ner 131 c oop embedded class
假设我必须使用C(没有C++或面向对象的编译器)并且我没有动态内存分配,我可以使用哪些技术来实现类,或者类的良好近似?将"类"隔离到单独的文件中总是一个好主意吗?假设我们可以通过假定固定数量的实例来预分配内存,或者甚至在编译时将每个对象的引用定义为常量.随意假设我将需要实施哪个OOP概念(它会有所不同)并为每个概念提出最佳方法.
限制:
unw*_*ind 83
这取决于您想要的确切的"面向对象"功能集.如果你需要重载和/或虚方法之类的东西,你可能需要在结构中包含函数指针:
typedef struct {
float (*computeArea)(const ShapeClass *shape);
} ShapeClass;
float shape_computeArea(const ShapeClass *shape)
{
return shape->computeArea(shape);
}
Run Code Online (Sandbox Code Playgroud)
这将允许您通过"继承"基类并实现一个合适的函数来实现一个类:
typedef struct {
ShapeClass shape;
float width, height;
} RectangleClass;
static float rectangle_computeArea(const ShapeClass *shape)
{
const RectangleClass *rect = (const RectangleClass *) shape;
return rect->width * rect->height;
}
Run Code Online (Sandbox Code Playgroud)
这当然要求您还要实现一个构造函数,以确保正确设置函数指针.通常,您会为实例动态分配内存,但您也可以让调用者也这样做:
void rectangle_new(RectangleClass *rect)
{
rect->width = rect->height = 0.f;
rect->shape.computeArea = rectangle_computeArea;
}
Run Code Online (Sandbox Code Playgroud)
如果你想要几个不同的构造函数,你将不得不"装饰"函数名,你不能有多个rectangle_new()
函数:
void rectangle_new_with_lengths(RectangleClass *rect, float width, float height)
{
rectangle_new(rect);
rect->width = width;
rect->height = height;
}
Run Code Online (Sandbox Code Playgroud)
这是一个显示用法的基本示例:
int main(void)
{
RectangleClass r1;
rectangle_new_with_lengths(&r1, 4.f, 5.f);
printf("rectangle r1's area is %f units square\n", shape_computeArea(&r1));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我希望至少可以给你一些想法.对于C中成功且丰富的面向对象框架,请查看glib的GObject库.
另请注意,上面没有明确的"类"建模,每个对象都有自己的方法指针,比通常在C++中找到的更灵活.此外,它需要内存.您可以通过在class
结构中填充方法指针来摆脱这种情况,并为每个对象实例创建一种引用类的方法.
ere*_*der 23
我不得不做一次做作业.我遵循这种方法:
一个简单的例子是这样的:
/// Queue.h
struct Queue
{
/// members
}
typedef struct Queue Queue;
void push(Queue* q, int element);
void pop(Queue* q);
// etc.
///
Run Code Online (Sandbox Code Playgroud)
Pet*_*ham 12
如果您只需要一个类,请使用struct
s 数组作为"对象"数据,并将指针传递给"成员"函数.您可以typedef struct _whatever Whatever
在声明struct _whatever
从客户端代码隐藏实现之前使用.这样的"对象"和C标准库FILE
对象之间没有区别.
如果您需要具有继承和虚函数的多个类,则通常将指向函数的指针作为结构的成员,或指向虚函数表的共享指针.该GObject的库同时使用这一点,typedef的把戏,而被广泛使用.
还有一本关于在线可用技术的书 - 使用ANSI C进行面向对象编程.
C接口和实现:创建可重用软件的技术, David R. Hanson
这本书很好地涵盖了你的问题.这是Addison Wesley专业计算系列.
基本范例是这样的:
/* for data structure foo */
FOO *myfoo;
myfoo = foo_create(...);
foo_something(myfoo, ...);
myfoo = foo_append(myfoo, ...);
foo_delete(myfoo);
Run Code Online (Sandbox Code Playgroud)
我将给出一个简单的示例,说明如何在C中完成OOP。我知道该主题来自2009年,但无论如何都希望添加。
/// Object.h
typedef struct Object {
uuid_t uuid;
} Object;
int Object_init(Object *self);
uuid_t Object_get_uuid(Object *self);
int Object_clean(Object *self);
/// Person.h
typedef struct Person {
Object obj;
char *name;
} Person;
int Person_init(Person *self, char *name);
int Person_greet(Person *self);
int Person_clean(Person *self);
/// Object.c
#include "object.h"
int Object_init(Object *self)
{
self->uuid = uuid_new();
return 0;
}
uuid_t Object_get_uuid(Object *self)
{ // Don't actually create getters in C...
return self->uuid;
}
int Object_clean(Object *self)
{
uuid_free(self->uuid);
return 0;
}
/// Person.c
#include "person.h"
int Person_init(Person *self, char *name)
{
Object_init(&self->obj); // Or just Object_init(&self);
self->name = strdup(name);
return 0;
}
int Person_greet(Person *self)
{
printf("Hello, %s", self->name);
return 0;
}
int Person_clean(Person *self)
{
free(self->name);
Object_clean(self);
return 0;
}
/// main.c
int main(void)
{
Person p;
Person_init(&p, "John");
Person_greet(&p);
Object_get_uuid(&p); // Inherited function
Person_clean(&p);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
基本概念涉及将“继承的类”放在结构的顶部。这样,访问结构中的前4个字节也将访问“继承的类”中的前4个字节(假定非疯狂的优化)。现在,当将结构体的指针强制转换为“继承的类”时,“继承的类”可以像通常访问其成员一样访问“继承的值”。
对于构造函数,析构函数,分配和释放函数的命名约定以及一些命名约定(我建议使用init,clean,new,free)将使您大有帮助。
对于虚函数,可以在结构中使用函数指针,可能使用Class_func(...); 包装器也。对于(简单的)模板,添加size_t参数来确定大小,需要void *指针或仅具有您所关注的功能的“类”类型。(例如int GetUUID(Object * self); GetUUID(&p);)