在面向对象的土地上挣扎?

时间:2009-03-23 18:47:34

标签: c procedural-programming

当我遇到编程问题时,我自然会开始将它们分解为我脑子里的逻辑对象。谁有责任,谁拥有什么,谁来自什么等等。

我正在与C斗争。我只是不知道如何用程序语言做事。

有经验的C程序员可以帮助解释我在设计时如何考虑我的程序吗?

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

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

有人可以帮我“搞定”吗?

相关what is the best way to plan and organize development of an application in c

10 个答案:

答案 0 :(得分:30)

  

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

没有

这样做。

  1. 定义你的课程但你觉得做OO设计很舒服。

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

  3. 将该结构放在头文件中,以及在该结构上运行的所有函数。确保MyStruct * self是所有这些“方法函数”的第一个参数。

  4. 使用方法函数的所有主体编写C模块。

  5. 穷人在C中的OO效果很好。只需遵守规则,将所有内容放入您需要的结构中 - 公共和私有实例变量 - 一切。

    通常,避免尝试首先使用私有变量。您没有OO编译器的全部功能,因此不要担心“私有”或“受保护”等低价值功能。

答案 1 :(得分:17)

我会修改S. Lott的答案,使用opaque pointer来执行结构成员的数据隐藏:

  1. 定义您的班级,但您需要使用普通的OO设计。
  2. 您班级的成员变量会进入C语言结构。
  3. 在头文件中,您不希望公开对象的成员变量(因为它们在OO语言中是“私有的”)。相反,使用不透明指针,即
    typedef struct mystruct_s *mystruct_t; // first argument to all your methods
  4. 对于您想要“公开”的所有方法,请将其签名放在.h文件中。方法体应该进入.c文件,“private”方法应该只在.c文件中定义,并且也声明为静态,因此它们的符号不会与其他文件中定义的符号冲突。
  5. 使用此方法不需要像下划线那样的聪明命名约定,但这意味着所有成员变量都是私有的。函数可以是公共函数或私有函数,但公共函数它们是全局命名空间的一部分,因此您可能希望使用“包”名称来限定其名称,如mystruct_push()mystruct_pop()等。

    如果来电者或图书馆负责致电malloc()free(),您还需要明确说明。您很可能会使用mystruct_t *create()void destroy(mystruct_t *target)方法。

答案 2 :(得分:15)

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

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

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

有一个很棒的pdf of object oriented programming with ANSI-C here

答案 3 :(得分:4)

我有一段时间从程序化到OO思考,所以我感到痛苦。

我发现为了学习如何创建对象,最好考虑一下它们对调用者的看法。从另一个方面来看,同样的方法可能对您有所帮助。考虑组件的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程序员已经没有管理过。

祝你好运!

答案 4 :(得分:2)

查看Lua C api。就C接口设计而言,它一直是我的指路明灯。每个函数都将Lua状态作为一个主要参数,成为你的“this”。继承有点棘手,但Chipmunk设法做了很好的工作,暴露了采用通用形状结构的函数,并计算出通过“klass”实际调用哪个函数的细节。您可以经常利用void *来使函数采用与OO中重载方式不同的类型(结构)。它有时会感觉有点hackish但效果很好。

答案 5 :(得分:1)

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

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

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

答案 6 :(得分:0)

我第二次提出做“穷人在C中的OO”的建议。我也认为你可能会花些时间看看Perl的OO是如何工作的。基本上OO是通过让解释器为每个方法提供实例作为隐式第一参数来完成的。您将希望在C中明确地执行相同的操作,并使用真正非常好的代码组织,因为编译器不会为您强制执行良好的封装。

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

答案 7 :(得分:0)

答案 8 :(得分:0)

您也可以使用C:

来创建派生类

Derived classes in C - What is your favorite method?

答案 9 :(得分:0)