But in C, there aren't objects. So do I have to inline all the behavior of my Queue data structure?
No.
Do this.
Define your class however you feel comfortable doing OO design.
Write the attributes of your class as a C-language struct.
Put that struct in a header file, along with all of the functions that operate on that struct. Make sure a MyStruct * self is the first argument to all of these "method functions".
Write a C module with all of the bodies of the method functions.
Poor-person's OO in C. It works well. Just be disciplined about putting everything into the struct that you need -- public and private instance variables -- everything.
Generally, avoid trying to have private variables in the first place. You don't have the full power of an OO compiler, so don't bother with low-value features like "private" or "protected".
I would amend S. Lott's answer to use an opaque pointer to perform data hiding of the members of the struct:
Define your class however you want using normal OO design.
Member variables of your class go into a C language struct.
In the header file, you do not want to expose the member variables of your object (since these would be "private" in an OO language). Instead, use an opaque pointer, i.e. typedef struct mystruct_s *mystruct_t; // first argument to all your methods
For all the methods you want to be "public", put their signatures in your .h file. Method bodies should go into the .c file, and "private" methods should be only defined in the .c file and also declared static so their symbols do not collide with symbols defined in other files.
Clever naming conventions like underscores are unnecessary using this method, but it means that all your member variables will be private. Functions can be public or private, although public functions they are part of a global namespace so you might want to qualify their names with a "package" name like mystruct_push(), mystruct_pop(), etc.
You also need to make it clear if the caller or the library is responsible for calling malloc() and free(). Most likely you will have mystruct_t *create() and void destroy(mystruct_t *target) methods.
我发现为了学习如何创建对象,最好考虑它们对调用者来说是什么样子。 同样的方法可能会对您有所帮助,但换一种方式。 考虑一下您的组件的 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 程序员都没有这样做。
祝你好运!
I had a bear of a time moving from procedural to OO thinking, so I feel your pain.
I found that in order to learn how to create objects, it was best to think about how they would look to the caller. The same approach might help you, going the other way. Consider what the API to your component would look like. One good way is to study existing C APIs (I used the standard Java APIs as a set of examples of an OO API).
You're used to using a queue component something like this:
import some.package.Queue;
Queue q = new Queue();
q.add(item);
In a typical C api, you'd expect something more like:
#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);
Whenever you find yourself thinking in objects, make a similar transform.
You can use pointers to functions and the like, to make object-like structures in C - but many thousands of C programmers have managed without.
查看 Lua C api。 就 C 接口设计而言,它一直是我的指路明灯。 每个函数都以 Lua 状态作为主要参数,它成为你的“this”。 继承有点棘手,但是 Chipmunk 设法做得很好,公开了需要通用形状结构并计算出实际通过“klass”调用哪个函数的细节。 您经常可以利用 void* 让函数采用不同的类型(结构),就像在 OO 中重载一样。 它有时会让人感觉有点黑客,但效果很好。
Check out the Lua C api. It has been my guiding light as far as C interface design goes. Every function takes a Lua state as a leading argument which becomes your "this". Inheritance is a little trickier but Chipmunk manages to do a pretty good job exposing functions that take a generic shape structs and work out the details of which function is actually called via a "klass". You can often exploit void* to have functions take different types (structs) the way you would overload in OO. It can feel a little hackish at times but works well.
使用访问器成员函数,例如getColor()、setColor()可以减轻内部差异并提供一些数据隐藏。 如果你真的想要,你可以使用 Brian Bondy 的使用宏隐藏的建议。
实际上有很多 FOSS 库提供了标准容器——哈希表、动态数组、链表等。
Originally C++ was just a compiler that wrote C code from the C++ sources; those were then compiled by the native C compiler, linked, etc.
Hence, all OOP methods are available in C -- it's just that the compiler won't help you, and does not provide all the compile-time capabilities such as templates, operator overrides, data hiding, etc.
The C++ "struct" was (probably still is) equivalent to a "class" with all members "public".
Member functions can be implemented as function pointers in a structure; this can provide encapsulation and polymorphism. Constructors exist in the global scope unless you're using factories. Destructors can be member functions.
Using accessor member functions, like getColor(), setColor() can alleviate internal differences and provide some data hiding. If you really want, you could use Brian Bondy's suggestion of hiding with macros.
There are actually a lot of FOSS libraries that provide standard containers -- hash tables, dynamic arrays, linked lists, etc.
我同意进行“Poor man's OO in C”的建议。 我还认为花一些时间了解 Perl 的面向对象是如何工作的可能会对您有所帮助。 基本上,OO 是在 Perl 中通过让解释器为每个方法提供实例作为隐式第一个参数来完成的。 您将希望在 C 中显式地执行相同的操作,并使用非常非常好的代码组织,因为编译器不会为您强制执行良好的封装。
顺便说一句,您可以使用不透明指针强制将结构成员保持私有。 我似乎记得 GNU 编程标准和建议包括一种技术,通过在传递时将所有内容基本上转换为 void*,然后使用 typedef 来命名应该传递的每种特定类型的不透明指针。 (即每个“类”)
I second the suggestions for doing "Poor man's OO in C." I also think you might benefit from taking some time to see how Perl's OO works. Basically OO is accomplished in Perl by having the interpreter supply every method with the instance as an implicit first parameter. You'll want to do the same in C, explicitly, and use really, really good code organization since the compiler will not enforce good encapsulation for you.
By the way, you can enforce keeping the members of your structs private by using opaque pointers. I seem to recall that the GNU programming standards and recommendations included a technique for doing this by basically casting everything to void* when it was passed around, then using typedefs to name each specific type of opaque pointer that was supposed to be passed. (i.e., each "class")
发布评论
评论(9)
不,
这样做。
按照您在进行 OO 设计时感觉舒服的方式定义您的类。
按照
将类的属性编写为 C 语言结构。
将该结构以及对该结构进行操作的所有函数一起放入头文件中。 确保
MyStruct * self
是所有这些“方法函数”的第一个参数。编写一个包含所有方法函数主体的 C 模块。
穷人的 C 语言面向对象。效果很好。 只要遵守纪律,将所有内容放入您需要的结构中(公共和私有实例变量)即可。
一般来说,首先避免尝试使用私有变量。 您没有 OO 编译器的全部功能,因此不必担心“私有”或“受保护”等低价值功能。
No.
Do this.
Define your class however you feel comfortable doing OO design.
Write the attributes of your class as a C-language struct.
Put that struct in a header file, along with all of the functions that operate on that struct. Make sure a
MyStruct * self
is the first argument to all of these "method functions".Write a C module with all of the bodies of the method functions.
Poor-person's OO in C. It works well. Just be disciplined about putting everything into the struct that you need -- public and private instance variables -- everything.
Generally, avoid trying to have private variables in the first place. You don't have the full power of an OO compiler, so don't bother with low-value features like "private" or "protected".
我会修改 S. Lott 的答案,使用 不透明指针 来执行成员的数据隐藏结构的:
typedef struct mystruct_s *mystruct_t; // 所有方法的第一个参数
使用此方法不需要像下划线这样的巧妙命名约定,但这意味着所有成员变量都将是私有的。 函数可以是公共的或私有的,尽管公共函数是全局命名空间的一部分,因此您可能需要使用“包”名称来限定它们的名称,例如
mystruct_push()
、mystruct_pop()< 。
您还需要明确调用者或库是否负责调用
malloc()
和free()
您很可能会有mystruct_t *create()
和void destroy(mystruct_t *target)
方法。I would amend S. Lott's answer to use an opaque pointer to perform data hiding of the members of the struct:
typedef struct mystruct_s *mystruct_t; // first argument to all your methods
Clever naming conventions like underscores are unnecessary using this method, but it means that all your member variables will be private. Functions can be public or private, although public functions they are part of a global namespace so you might want to qualify their names with a "package" name like
mystruct_push()
,mystruct_pop()
, etc.You also need to make it clear if the caller or the library is responsible for calling
malloc()
andfree()
. Most likely you will havemystruct_t *create()
andvoid destroy(mystruct_t *target)
methods.您仍然可以用 C 来思考面向对象。
您只需要创建一个结构体和一组函数,这些函数将指向该结构体实例的指针作为其第一个参数。
至于多态性,您可以将结构体的大小作为结构体的第一个成员传递,这样您就知道如何转换它。
这里有一个很棒的 使用 ANSI-C 进行面向对象编程的 pdf。
You can still think object oriented with C.
You just need to create a struct and a set of functions that take a pointer to an instance of that struct as its first parameter.
As for polymorphism, you can pass the size of the struct as the first member of the struct, so you know how to cast it.
There is a great pdf of object oriented programming with ANSI-C here.
我经历了一段从过程思维转向面向对象思维的过程,所以我能感受到你的痛苦。
我发现为了学习如何创建对象,最好考虑它们对调用者来说是什么样子。 同样的方法可能会对您有所帮助,但换一种方式。 考虑一下您的组件的 API 是什么样子的。 一个好方法是研究现有的 C API(我使用标准 Java API 作为 OO API 的一组示例)。
您习惯于使用这样的队列组件:
在典型的 C api 中,您会期望更像这样的东西:
每当您发现自己在对象中思考时,请进行类似的转换。
您可以使用指向函数等的指针,在 C 中创建类似对象的结构 - 但成千上万的 C 程序员都没有这样做。
祝你好运!
I had a bear of a time moving from procedural to OO thinking, so I feel your pain.
I found that in order to learn how to create objects, it was best to think about how they would look to the caller. The same approach might help you, going the other way. Consider what the API to your component would look like. One good way is to study existing C APIs (I used the standard Java APIs as a set of examples of an OO API).
You're used to using a queue component something like this:
In a typical C api, you'd expect something more like:
Whenever you find yourself thinking in objects, make a similar transform.
You can use pointers to functions and the like, to make object-like structures in C - but many thousands of C programmers have managed without.
Good luck!
查看 Lua C api。 就 C 接口设计而言,它一直是我的指路明灯。 每个函数都以 Lua 状态作为主要参数,它成为你的“this”。 继承有点棘手,但是 Chipmunk 设法做得很好,公开了需要通用形状结构并计算出实际通过“klass”调用哪个函数的细节。 您经常可以利用 void* 让函数采用不同的类型(结构),就像在 OO 中重载一样。 它有时会让人感觉有点黑客,但效果很好。
Check out the Lua C api. It has been my guiding light as far as C interface design goes. Every function takes a Lua state as a leading argument which becomes your "this". Inheritance is a little trickier but Chipmunk manages to do a pretty good job exposing functions that take a generic shape structs and work out the details of which function is actually called via a "klass". You can often exploit void* to have functions take different types (structs) the way you would overload in OO. It can feel a little hackish at times but works well.
最初,C++ 只是一个从 C++ 源代码编写 C 代码的编译器; 然后,它们由本机 C 编译器进行编译、链接等。
因此,所有 OOP 方法都可以在 C 中使用——只是编译器不会帮助您,并且不提供所有编译时功能,例如模板、运算符覆盖、数据隐藏等。C
Originally C++ was just a compiler that wrote C code from the C++ sources; those were then compiled by the native C compiler, linked, etc.
Hence, all OOP methods are available in C -- it's just that the compiler won't help you, and does not provide all the compile-time capabilities such as templates, operator overrides, data hiding, etc.
我同意进行“Poor man's OO in C”的建议。 我还认为花一些时间了解 Perl 的面向对象是如何工作的可能会对您有所帮助。 基本上,OO 是在 Perl 中通过让解释器为每个方法提供实例作为隐式第一个参数来完成的。 您将希望在 C 中显式地执行相同的操作,并使用非常非常好的代码组织,因为编译器不会为您强制执行良好的封装。
顺便说一句,您可以使用不透明指针强制将结构成员保持私有。 我似乎记得 GNU 编程标准和建议包括一种技术,通过在传递时将所有内容基本上转换为 void*,然后使用 typedef 来命名应该传递的每种特定类型的不透明指针。 (即每个“类”)
I second the suggestions for doing "Poor man's OO in C." I also think you might benefit from taking some time to see how Perl's OO works. Basically OO is accomplished in Perl by having the interpreter supply every method with the instance as an implicit first parameter. You'll want to do the same in C, explicitly, and use really, really good code organization since the compiler will not enforce good encapsulation for you.
By the way, you can enforce keeping the members of your structs private by using opaque pointers. I seem to recall that the GNU programming standards and recommendations included a technique for doing this by basically casting everything to void* when it was passed around, then using typedefs to name each specific type of opaque pointer that was supposed to be passed. (i.e., each "class")
您也可以使用 C 来执行派生类:
C 中的派生类 -你最喜欢的方法是什么?
You can do derived classes also with C:
Derived classes in C - What is your favorite method?
使用 glib、c 库和 oop
http://library.gnome.org/devel/glib/2.20/
use glib, c library with oop
http://library.gnome.org/devel/glib/2.20/