19

当我遇到编程问题时,我自然会开始在脑海中将它们分解为逻辑对象。谁有什么责任,谁拥有什么,谁从什么得到什么,等等。

我在 C 中苦苦挣扎。我只是不知道如何用程序语言做事。

有经验的 C 程序员能否帮助解释我在设计时应该如何考虑我的程序?

例如,我想编写自己的 Semaphore 类。我的程序自然需要一个队列数据结构,我也想自己编写。如果我需要在 Java 或 C# 中执行此操作,我可以简单地快速创建一个 Queue 类并在我的 Semaphore 类中创建它的一个新实例。

但是在 C 中,没有对象。那么我是否必须内联队列数据结构的所有行为?

有人可以帮我“得到它”吗?

相关在 c 中计划和组织应用程序开发的最佳方式是什么

4

9 回答 9

31

但是在 C 中,没有对象。那么我是否必须内联队列数据结构的所有行为?

不。

做这个。

  1. 定义你的类,但是你觉得做 OO 设计很舒服。

  2. 将类的属性写为 C 语言结构。

  3. 将该结构与对该结构进行操作的所有函数一起放入头文件中。确保 aMyStruct * self是所有这些“方法函数”的第一个参数。

  4. 编写一个包含所有方法函数主体的 C 模块。

C语言中的穷人的OO。它运行良好。只需遵守规则,将所有内容放入您需要的结构中——公共和私有实例变量——所有内容。

一般来说,首先要避免尝试使用私有变量。您没有 OO 编译器的全部功能,因此不要为诸如“私有”或“受保护”之类的低价值特性而烦恼。

于 2009-03-23T18:57:49.873 回答
17

我会修改 S. Lott 的答案以使用不透明指针来执行结构成员的数据隐藏:

  1. 使用正常的 OO 设计定义您想要的类。
  2. 您的类的成员变量进入 C 语言结构。
  3. 在头文件中,您不想公开对象的成员变量(因为这些变量在 OO 语言中是“私有的”)。相反,使用不透明的指针,即
    typedef struct mystruct_s *mystruct_t; // first argument to all your methods
  4. 对于您想要“公开”的所有方法,请将它们的签名放在您的 .h 文件中。方法体应该放在 .c 文件中,“私有”方法应该只在 .c 文件中定义,并且声明为静态的,这样它们的符号就不会与其他文件中定义的符号冲突。

使用此方法不需要像下划线这样的巧妙命名约定,但这意味着您的所有成员变量都是私有的。函数可以是公共的或私有的,尽管公共函数是全局命名空间的一部分,因此您可能希望使用“包”名称来限定它们的名称,例如mystruct_push(),mystruct_pop()等。

您还需要明确调用者或库是否负责调用malloc()and free()。很可能您将拥有mystruct_t *create()void destroy(mystruct_t *target)方法。

于 2009-03-23T19:19:39.137 回答
15

你仍然可以用 C 来思考面向对象。

您只需要创建一个结构和一组函数,这些函数将指向该结构实例的指针作为其第一个参数。

至于多态性,您可以将结构的大小作为结构的第一个成员传递,因此您知道如何转换它。

这里有一个很棒的 ANSI-C 面向对象编程的 pdf 文件

于 2009-03-23T18:49:29.783 回答
4

从程序思维转向面向对象思维,我经历了一段时间的痛苦,所以我感受到了你的痛苦。

我发现为了学习如何创建对象,最好考虑调用者对它们的看法。同样的方法可能会对您有所帮助,但反过来。考虑一下你的组件的 API 是什么样的。一个好方法是研究现有的 C API(我使用标准 Java API 作为 OO API 的一组示例)。

你习惯使用这样的队列组件:

import some.package.Queue;

Queue q = new Queue(); 
q.add(item);

在典型的 C api 中,您会期望更多类似于:

#include <queue.h> // provides queue, make_queue(), queue_add(), others

queue q = make_queue(); // queue is probably a struct, or a struct*
queue_add(q,item);

每当您发现自己在思考对象时,请进行类似的变换。

您可以使用指向函数等的指针,在 C 中创建类似对象的结构 - 但是成千上万的 C 程序员已经在没有的情况下进行了管理。

祝你好运!

于 2009-03-23T20:10:34.207 回答
2

查看Lua C api。就 C 接口设计而言,它一直是我的指路明灯。每个函数都将 Lua 状态作为主要参数,成为你的“this”。继承有点棘手,但Chipmunk设法很好地公开了采用通用形状结构的函数,并计算出通过“klass”实际调用的函数的细节。您通常可以利用 void* 让函数采用不同的类型(结构),就像在 OO 中重载一样。有时它可能会让人觉得有点骇人听闻,但效果很好。

于 2009-03-23T19:51:46.593 回答
1

最初 C++ 只是一个从 C++ 源代码编写 C 代码的编译器;然后由本机 C 编译器编译、链接等。

因此,所有 OOP 方法在 C 中都可用——只是编译器不会帮助您,并且不提供所有编译时功能,例如模板、运算符覆盖、数据隐藏等。

  1. C++“结构”(可能仍然是)等同于所有成员“公共”的“类”。
  2. 成员函数可以实现为结构中的函数指针;这可以提供封装和多态性。 除非您使用工厂,否则构造函数存在于全局范围内。 析构函数可以是成员函数。
  3. 使用访问器成员函数,如getColor()setColor()可以缓解内部差异并提供一些数据隐藏。如果你真的想要,你可以使用 Brian Bondy 的隐藏宏的建议。
  4. 实际上有很多提供标准容器的 FOSS 库——哈希表、动态数组、链表等。
于 2009-03-23T22:37:20.347 回答
0

我赞同做“C 中穷人的 OO”的建议。我还认为您可能会受益于花一些时间来了解 Perl 的 OO 是如何工作的。基本上,在 Perl 中,OO 是通过让解释器为每个方法提供实例作为隐式第一个参数来实现的。您将希望在 C 中显式地做同样的事情,并使用非常非常好的代码组织,因为编译器不会为您强制执行良好的封装。

顺便说一句,您可以通过使用不透明指针来强制保持结构的成员私有。我似乎记得 GNU 编程标准和建议包括一种技术,通过在传递时将所有内容基本上强制转换为 void*,然后使用 typedef 来命名应该传递的每种特定类型的不透明指针。(即,每个“类”)

于 2009-03-23T19:34:24.107 回答
0

你也可以用 C 做派生类:

C 中的派生类 - 你最喜欢的方法是什么?

于 2009-03-24T00:17:40.620 回答
0

使用 glib,带有 oop 的 c 库 http://library.gnome.org/devel/glib/2.20/

于 2009-04-05T04:03:46.713 回答