C语言 C中的面向对象编程

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2181079/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 04:21:35  来源:igfitidea点击:

Object oriented programming in C

coop

提问by Chinmay Kanchi

Possible Duplicates:
Can you write object oriented code in C?
Object Oriented pattern in C ?

可能的重复:
你能用 C 编写面向对象的代码吗?
C中的面向对象模式?

I remember reading a while ago about someone (I think it was Linus Torvalds) talking about how C++ is a horrible language and how you can write object-oriented programs with C. On having had time to reflect, I don't really see how allobject oriented concepts carry over into C. Some things are fairly obvious. For example:

我记得不久前读到有人(我认为是 Linus Torvalds)谈论 C++ 是一种可怕的语言,以及如何用 C 编写面向对象的程序。有时间反思,我真的不明白所有面向对象的概念都继承到 C 中。有些事情是相当明显的。例如:

  1. To emulate member functions, you can put function pointers in structs.
  2. To emulate polymorphism, you can write a function that takes a variable number of arguments and do some voodoo depending on, say, the sizeofthe parameter(s)
  1. 要模拟成员函数,您可以将函数指针放在结构中。
  2. 要模拟多态,你可以写一个函数,它的参数个数可变,并取决于做一些巫术,也就是说,sizeof参数(S)

How would you emulate encapsulation and inheritance though?

但是,您将如何模拟封装和继承?

I suppose encapsulation could sort of be emulated by having a nested struct that stored private members. It would be fairly easy to get around, but could perhaps be named PRIVATEor something equally obvious to signal that it isn't meant to be used from outside the struct. What about inheritance though?

我想封装可以通过具有存储私有成员的嵌套结构来模拟。绕过它会很容易,但也许可以命名PRIVATE或同样明显的东西来表明它不打算从结构外部使用。不过继承呢?

回答by Tronic

You can implement polymorphism with regular functions and virtual tables (vtables). Here's a pretty neat system that I invented (based on C++) for a programming exercise: alt text

您可以使用常规函数和虚拟表 (vtables) 实现多态。这是我为编程练习发明的一个非常简洁的系统(基于 C++):替代文字

The constructors allocate memory and then call the class's init function where the memory is initialized. Each init function should also contain a static vtable struct that contains the virtual function pointers (NULL for pure virtual). Derived class init functions call the superclass init function before doing anything else.

构造函数分配内存,然后调用类的 init 函数来初始化内存。每个 init 函数还应该包含一个静态 vtable 结构,其中包含虚拟函数指针(纯虚拟为 NULL)。派生类 init 函数在执行任何其他操作之前调用超类 init 函数。

A very nice API can be created by implementing the virtual function wrappers (not to be confused with the functions pointed to by the vtables) as follows (add static inlinein front of it, if you do this in the header):

通过实现虚函数包装器(不要与虚表指向的函数混淆),可以创建一个非常好的 API,如下所示(static inline如果在标题中执行此操作,则在其前面添加):

int playerGuess(Player* this) { return this->vtable->guess(this); }

Single inheritance can be done by abusing the binary layout of a struct: alt text

可以通过滥用结构的二进制布局来实现单继承: 替代文字

Notice that multiple inheritance is messier as then you often need to adjust the pointer value when casting between types of the hierarchy.

请注意,多重继承比较混乱,因为在层次结构类型之间进行转换时,您经常需要调整指针值。

Other type-specific data can be added to the virtual tables as well. Examples include runtime type info (e.g. type name as a string), linking to superclass vtable and the destructor chain. You probably want virtual destructors where derived class destructor demotes the object to its super class and then recursively calls the destructor of that and so on, until the base class destructor is reached and that finally frees the struct.

其他特定于类型的数据也可以添加到虚拟表中。示例包括运行时类型信息(例如,作为字符串的类型名称)、链接到超类 vtable 和析构函数链。您可能需要虚拟析构函数,其中派生类析构函数将对象降级为其超类,然后递归调用它的析构函数,依此类推,直到到达基类析构函数并最终释放结构。

Encapsulationwas done by defining the structs in player_protected.h and implementing the functions (pointed to by the vtable) in player_protected.c, and similarly for derived classes, but this is quite clumsy and it degrades performance (as virtual wrappers cannot be put to headers), so I would recommend against it.

封装是通过在 player_protected.h 中定义结构体并在 player_protected.c 中实现函数(由 vtable 指向)来完成的,对于派生类也是如此,但这非常笨拙,并且会降低性能(因为虚拟包装器不能放在标头),所以我建议不要这样做。

回答by Kornel Kisielewicz

Have you read the "bible" on the subject? See Object Oriented C...

你读过关于这个主题的“圣经”吗?请参阅面向对象的 C...

回答by John Knoeller

How would you emulate encapsulation and inheritance though?

但是,您将如何模拟封装和继承?

Actually, encapsulation is the easiest part. Encapsulation is a designphilosophy, it has nothing at all to do with the language and everything to to with how you think about problems.

实际上,封装是最​​简单的部分。封装是一种设计哲学,它与语言完全无关,而与您如何思考问题有关。

For example, the Windows FILE api is completely encapsulated. When you open a file, you get back an opaque object that contains all of the state information for the file 'object'. You hand this handle back to each of the file io apis. The encapsulation is actually muchbetter than C++ because there is no public header file that people can look at and see the names of your private variables.

例如,Windows FILE api 是完全封装的。当你打开一个文件时,你会得到一个不透明的对象,它包含文件“对象”的所有状态信息。您将此句柄交还给每个文件 io apis。封装实际上是优于C ++,因为没有公共的头文件,人们可以看看,看看你的私有变量的名称。

Inheritance is harder, but it isn't at all necessary in order for your code to be object oriented. In some ways aggregation is better than inheritance anyway, and aggregation is just as easy in C as in C++. see thisfor instance.

继承更难,但它并不是面向对象的代码所必需的。在某些方面,聚合无论如何都比继承要好,而且聚合在 C 中和在 C++ 中一样容易。看看这个

In response to Neil see Wikipediafor an explanation of why inheritance isn't necessary for polymorphism.

作为对 Neil 的回应,请参阅Wikipedia以解释为什么多态性不需要继承。

Us old-timers wrote object oriented code years before C++ compilers were available, it's a mind-set not a tool-set.

我们老手在 C++ 编译器可用之前几年就编写了面向对象的代码,这是一种思维方式而不是工具集。

回答by Chuck

Apple's C-based CoreFoundation framework was actually written so that its "objects" could double as objects in Objective-C, an actual OO language. A fairly large subset of the framework is open source on Apple's site as CF-Lite. Might be a useful case study in a major OS-level framework done this way.

Apple 的基于 C 的 CoreFoundation 框架实际上是这样编写的,它的“对象”可以兼作 Objective-C(一种实际的面向对象语言)中的对象。该框架的一个相当大的子集在 Apple 的网站上作为CF-Lite开源。在以这种方式完成的主要操作系统级框架中可能是一个有用的案例研究。

回答by mloskot

From a little bit higher altitude and considering the problem rather more open-minded than as the OOP mainstream may suggest, Object-Oriented Programming means thinking about objects as of data with associated functions. It does not necessarily mean an function has to be physically attached to an object as it is in popular languages which support paradigm of OOP, for instance in C++:

从更高的高度并考虑到比 OOP 主流所建议的更开放的问题,面向对象编程意味着将对象视为具有相关功能的数据。这并不一定意味着函数必须物理附加到对象,因为它在支持 OOP 范式的流行语言中,例如在 C++ 中:

struct T
{
   int data;
   int get_data() const { return data; }
};

I would suggest to take a closer look at GTK+ Object and Type System. It is a brilliant example of OOP realised in C programming language:

我建议仔细看看GTK+ Object and Type System。这是一个用 C 语言实现的 OOP 的绝妙例子:

GTK+ implements its own custom object system, which offers standard object-oriented features such as inheritance and virtual function

GTK+ 实现了自己的自定义对象系统,它提供了标准的面向对象特性,例如继承和虚函数

The association can also be contractual and conventional.

协会也可以是契约性的和约定俗成的。

Regarding encapsulation and data hiding techniques, popular and simple one may be Opaque Pointer(or Opaque Data Type) - you can pass it around but in order to load or store any information, you have to call associated function which knows how to talk to the object hidden behind that opaque pointer.

关于封装和数据隐藏技术,流行和简单的可能是Opaque Pointer(或 Opaque Data Type)——您可以传递它,但为了加载或存储任何信息,您必须调用相关的函数,该函数知道如何与隐藏在该不透明指针后面的对象。

Another one, similar but different is Shadow Data type- check this link where Jon Jaggergives excellent explanation of this not-so-well-known-technique.

另一个类似但不同的是Shadow Data 类型- 检查这个链接,其中Jon Jagger对这种不太知名的技术给出了很好的解释。

回答by Duncan

Definitely look at Objective-C.

绝对看看Objective-C。

typedef struct objc_object {
    Class isa;
} *id;

typedef struct objc_class {
    struct objc_class *isa;
    struct objc_class *super_class
    const char *name;
    long version;
    long info
    long instance_size;
    struct objc_ivar_list *ivars;
    struct objc_method_list **methodLists;
   struct objc_cache *cache;
   struct objc_protocol_list *protocols;
} *Class;

As you can see, inheritance information, along with other details is held in a class struct (conveniently the class can also be treated as an object).

如您所见,继承信息以及其他详细信息保存在类结构中(方便地,类也可以被视为对象)。

Objective-C suffers in the same manner as C++ with encapsulation in that you need to declare your variables publicly. Straight C is much more flexible in that you can just return void pointers that only your module has internal access to, so in that respect encapsulation is much better.

Objective-C 与 C++ 的封装方式相同,因为您需要公开声明变量。直接 C 更灵活,因为您可以只返回只有您的模块可以内部访问的空指针,因此在这方面封装要好得多。

I once wrote a basic OO style C paint program as part of a graphics course - I didn't go as far as the class declaration, I simply used a vtable pointer as the first element of the struct and implemented hand-coded inheritance. The neat thing about playing around with vtables at such a low level is that you can change class behaviour at runtime by changing a few pointers, or change on objects class dynamically. It was quite easy to create all sorts of hybrid objects, fake multiple inheritance, etc.

我曾经在图形课程中编写了一个基本的 OO 风格的 C 绘画程序——我没有深入到类声明,我只是使用了一个 vtable 指针作为结构的第一个元素并实现了手工编码的继承。在如此低的级别上使用 vtable 的巧妙之处在于,您可以在运行时通过更改一些指针或动态更改对象类来更改类的行为。创建各种混合对象、假多重继承等非常容易。

回答by higgo

Nice article & discussion regarding Objective-C here:

关于 Objective-C 的好文章和讨论在这里:

http://cocoawithlove.com/2009/10/objective-c-niche-why-it-survives-in.html

http://cocoawithlove.com/2009/10/objective-c-niche-why-it-survives-in.html

回答by caf

Take a look at the way the VFS layer works in the Linux kernel for an example of an inheritance pattern. The file operations for the various filesystems "inherit" a set of generic file operations functions (eg generic_file_aio_read(), generic_file_llseek()...), but can override them with their own implementations (eg. ntfs_file_aio_write()).

查看 VFS 层在 Linux 内核中的工作方式,以获取继承模式的示例。各种文件系统的文件操作“继承”了一组通用文件操作函数(例如generic_file_aio_read()generic_file_llseek()...),但可以用它们自己的实现(例如ntfs_file_aio_write())覆盖它们。

回答by Chris H

the gtk and glib libraries use macros to cast objects to various types.
add_widget(GTK_WIDGET(myButton));
I can't say how it's done but you can read their source to find out exactly how it's done.

gtk 和 glib 库使用宏将对象转换为各种类型。
add_widget(GTK_WIDGET(myButton));
我不能说它是如何完成的,但您可以阅读他们的来源以确切了解它是如何完成的。

回答by DarenW

For a great example of object-oriented programming in C, look at the source of POV-Ray from several years ago - version 3.1g is particularly good. "Objects" were struct with function pointers, of course. Macros were used to provide the core methods and data for an abstract object, and derived classes were structs that began with that macro. There was no attempt to deal with private/public, however. Things to be seen were in .h files and implementation details were in .c files, mostly, except for a lot of exceptions.

对于 C 中面向对象编程的一个很好的例子,请查看几年前 POV-Ray 的来源 - 3.1g 版特别好。当然,“对象”是带有函数指针的结构。宏用于为抽象对象提供核心方法和数据,派生类是以该宏开头的结构。然而,没有尝试处理私人/公共问题。要看到的东西在 .h 文件中,实现细节在 .c 文件中,大多数情况下,除了很多例外。

There were some neat tricks that I don't see how could be carried over to C++ - such as converting one class to a different but similar one on the fly just by reassigning function pointers. Simple for today's dynamic languages. I forgot the details; I think it might have been CSG intersection and union objects.

有一些巧妙的技巧,我不知道如何将其转移到 C++ 中——例如仅通过重新分配函数指针,将一个类即时转换为不同但相似的类。对于今天的动态语言来说很简单。我忘记了细节;我认为它可能是 CSG 交集和联合对象。

http://www.povray.org/

http://www.povray.org/