我知道一些更高级的语言,都是基于网络的(PHP、javascript、一些 python)。我终于决定学习一门较低级别的语言,并决定使用 C。问题是我使用的所有语言都严重基于 OOP。看到(基于我所做的研究)C 没有类或继承。因此,我问你这个问题:我应该如何以像 OOP 那样有条理的方式组织我的 C 代码,而不必切换语言或只拥有包含无穷无尽功能的文件?
来自未来的编辑:这个问题回想起来很愚蠢。我15岁,在我的学校没有CS...
C 并没有提供太多的代码组织方式。有函数和文件。
每当你想用隐藏实现或私有函数实现接口时,创建两个文件,如“foo.h”和“foo.c”。
接口和/或公共函数是“foo.h”中的函数原型,函数写在“foo.c”中。此外,任何隐藏的实现或其他私有函数都在“foo.c”中,但标记为static
,例如static int foo_internal_function(int i);
。标记的函数static
只能在它们所在的文件中引用,不能在任何其他文件中引用。
没有正式的命名空间,尽管您可以使用前缀函数名称获得几乎相同的效果,例如Foo_discombobulate_developers()
. 没有类,尽管您至少可以使用文件获得一些封装效果。没有可扩展的层次结构,只有文件和函数。
C 没有任何地方可以组织后来的语言,但是已经用它编写了许多非常好的程序。小心,注释任何令人困惑的东西(在 C 中可能会有令人困惑的东西),并做好笔记。
“功能无穷的文件”
您真正想要的是具有定义明确且功能有限的文件。这称为模块化编程。这个想法是您将基于功能的函数分组到单个编译单元中,并在标题中定义函数原型:
富.h:
int GetFoo();
Foo.c:
int GetFoo()
{
...
}
它有点类似于如何将一组方法分组到一个类中。主要区别在于,在任何给定时间,您可能正在或可能没有“这个”事情。也就是说,您仍然可以在 C 中进行本质上的“面向对象”编程。但是,您的对象成为模块的参数。这是一种方法:
酒吧.h:
typedef int BAR_OBJ
BAR_OBJ MakeBar();
int GetBarSize(BAR_OBJ);
void DoFooToBar(BAR_OBJ, ...)
巴.c
struct BarDetails
{
int isFree;
int size;
...
// other info about bar
};
static BarDetails arrayOfBars[...]; //
BAR_OBJ MakeBar()
{
// search BarDetails array, find free Bar
}
int GetBarSize(BAR_OBJ obj)
{
return arrayOfBars[obj];
}
void DoFooToBar(BAR_OBJ, ...)
{
// lookup bar obj and do something
}
好吧,如果您仍然想做一些 OO,请使用 C++。
如果您真的只想使用 C 而没有任何 OO,那么将您的逻辑划分为多个文件,并将每个源文件视为一个对象。因此,每个文件将只包含与该单一结构密切相关的结构和功能。
下面是一个在 C 中频繁出现的模仿 OOP 的模式:
考虑一个名为 MyClass 的类。
/* MyClass.h or myclass.h */
#ifndef MYCLASS_H
#define MYCLASS_H
struct myclass_s;
typedef struct myclass_s myclass_t;
myclass_t * myclass_new();
void delete_myclass(myclass_t *);
// Method int doMyStuff(int arg1,int arg2)
int myclass_doMyStuff(myclass_t *, int arg1, int arg2);
#endif //MYCLASS_H
头文件定义了类型 myclass_t,但隐藏了实际的实现 myclass_s。具有两个名称的这种有点公认的要求源于 C 在单独的命名空间中具有结构,而在 C++ 中,结构与所有其他类型位于相同的命名空间中。该代码旨在在 C 和 C++ 中工作。这是相应的 .c 文件:
/* MyClass.c or myclass.c */
#include "myclass.h" // Or MyClass.h
struct myclass_s {
int exampleField;
};
myclass_t * myclass_new()
{
myclass_t * o=(myclass_t*)malloc(sizeof(myclass_t));
// Initialize o here.
return o;
}
void myclass_delete(myclass_t * o)
{
// Do any cleanup needed on o here.
free(o);
}
int myclass_doMyStuff(myclass_t * o,int arg1,int arg2)
{
// ...
}
继承和动态绑定也可以在 C 中完成,但它们涉及更多。上述模式,或任何模拟 OOP 的模式,并不总是在 C 中做事的最佳方式,所以尽量不要沉迷于以类为中心的思维方式。不过,这种模式偶尔还是有用的。例如,libpng 使用了与此类似的东西(他们也使用 setjmp/longjmp 执行“异常”,我建议反对)。
有面向 OOP-ish C 的GObject,但精心设计的结构化程序不应该比等效的 OOP 程序更难阅读/维护。