3

我知道一些更高级的语言,都是基于网络的(PHP、javascript、一些 python)。我终于决定学习一门较低级别的语言,并决定使用 C。问题是我使用的所有语言都严重基于 OOP。看到(基于我所做的研究)C 没有类或继承。因此,我问你这个问题:我应该如何以像 OOP 那样有条理的方式组织我的 C 代码,而不必切换语言或只拥有包含无穷无尽功能的文件?

来自未来的编辑:这个问题回想起来很愚蠢。我15岁,在我的学校没有CS...

4

5 回答 5

18

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 中可能会有令人困惑的东西),并做好笔记。

于 2009-09-25T21:19:36.260 回答
9

“功能无穷的文件”

您真正想要的是具有定义明确且功能有限的文件。这称为模块化编程。这个想法是您将基于功能的函数分组到单个编译单元中,并在标题中定义函数原型:

富.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
   }
于 2009-09-25T21:08:43.977 回答
4

好吧,如果您仍然想做一些 OO,请使用 C++。

如果您真的只想使用 C 而没有任何 OO,那么将您的逻辑划分为多个文件,并将每个源文件视为一个对象。因此,每个文件将只包含与该单一结构密切相关的结构和功能。

于 2009-09-25T21:08:35.677 回答
4

下面是一个在 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 执行“异常”,我建议反对)。

于 2009-09-25T22:36:04.500 回答
1

有面向 OOP-ish C 的GObject,但精心设计的结构化程序不应该比等效的 OOP 程序更难阅读/维护。

于 2009-09-25T21:17:16.827 回答