C似乎有自己的准对象,例如“结构”,可以将其视为对象(以我们通常认为的高级方式)。
而且,C文件本身基本上是单独的“模块”,对吗?那模块不是也像“对象”吗?我对为什么C(看上去与C ++如此相似)为何被视为低级“过程”语言感到困惑,而C ++却是高级“面向对象”语言。
*编辑:(澄清)为什么和在哪里,画线,什么是“对象”,什么不是?
C似乎有自己的准对象,例如“结构”,可以将其视为对象(以我们通常认为的高级方式)。
而且,C文件本身基本上是单独的“模块”,对吗?那模块不是也像“对象”吗?我对为什么C(看上去与C ++如此相似)为何被视为低级“过程”语言感到困惑,而C ++却是高级“面向对象”语言。
*编辑:(澄清)为什么和在哪里,画线,什么是“对象”,什么不是?
Answers:
C似乎有自己的准对象,例如“结构”,可以将其视为对象
让我们和您一起通读有关面向对象编程的Wikipedia页面,并检查与传统上认为是面向对象的样式相对应的C样式结构的功能:
(OOP)是一种使用“对象”的编程范例-由数据字段和方法及其相互作用组成的数据结构
C结构是否由字段和方法以及它们的相互作用组成?没有。
编程技术可以包括诸如数据抽象,封装,消息传递,模块化,多态性和继承之类的功能。
C结构是否以“一流”的方式来做这些事情?不会。语言在您使用的所有步骤中都会对您不利。
面向对象的方法鼓励程序员将数据放置在程序其余部分无法直接访问的位置
C结构会这样做吗?没有。
面向对象的程序通常将包含不同类型的对象,每种类型对应于要管理的特定类型的复杂数据,或者可能对应于实际对象或概念
C结构会这样做吗?是。
可以将对象视为将其数据包装在旨在确保适当使用数据的一组函数中
没有。
每个对象都能够接收消息,处理数据以及向其他对象发送消息
结构本身可以发送和接收消息吗?否。可以处理数据吗?没有。
OOP数据结构倾向于“随身携带他们自己的运算符”
这会在C中发生吗?没有。
动态调度...封装...子类型多态性...对象继承...打开递归...对象类...类的实例...作用于附加对象的方法...消息传递... 。抽象
C结构有这些功能吗?没有。
您确切地认为结构的哪些特征是“面向对象的”?因为我找不到任何比一个事实,即结构定义其他类型。
现在,您当然可以使结构的字段成为函数的指针。您可以使结构具有字段,这些字段是指向函数指针数组的指针,与虚拟方法表相对应。等等。当然,您可以在C中模拟 C ++。您最好只使用C ++。
而且,C文件本身基本上是单独的“模块”,对吗?那模块不是也像“对象”吗?
同样,您认为模块的哪些特征使它们像对象一样起作用?模块是否支持抽象,封装,消息传递,模块化,多态和继承?
抽象和封装非常薄弱。显然,模块是模块化的。这就是为什么它们被称为模块。消息传递?仅在方法调用是消息的意义上,模块可以包含方法。多态性?不。遗产?不。模块是“对象”的较弱候选者。
关键字是“面向”而不是“对象”。即使使用对象但像结构一样使用对象的C ++代码也不是面向对象的。
C和C ++都可以进行OOP(C中没有访问控制),但是C中的语法不方便(至少可以这样说),而C ++中的语法则非常吸引人。尽管在这方面核心功能几乎相同,但C面向过程,而C ++面向对象。
使用对象来实现只能用对象完成设计的代码(通常意味着利用多态性)是面向对象的代码。使用对象仅比数据包多一点的代码,甚至使用面向对象语言的继承,实际上只是过程代码,它比需要的复杂。C语言中使用的函数指针在运行时随数据变化的结构而变化,这有点像是多态的,甚至可以说是面向对象的,即使是面向过程的语言。
基于最高级别的负责人:
对象是一种以相互链接的方式封装的数据和行为,这样它们就可以作为一个整体进行操作,并且可以实例化多次,并且如果您知道外部接口,则可以作为黑盒工作。
结构包含数据但没有任何行为,因此不能视为对象。
模块既包含行为也包含数据,但没有以相互关联的方式进行封装,当然不能多次实例化。
那是在您进行继承和多态之前...
“结构”仅是数据。通常,对“面向对象”的快速而肮脏的测试是:“是否存在一种允许将代码和数据封装为单个单元的结构?”。C失败了,因此是程序上的。C ++通过了该测试。
this
指针,但是在那种情况下,数据和处理数据的方法将封装在结构内部。
#include
d,减去未有条件地包含在内的所有内容(#if
,#ifdef
等)。
C与C ++一样,具有提供Data Abstraction的能力,这是之前存在的面向对象编程范例的一种习语。
C ++中的OOP扩展了抽象数据的方法。有人说它是有害的,而另一些人则认为正确使用它是一个好工具。
但是,您会发现许多C的“黑客”都在宣讲C如何完全有能力进行适当数量的抽象,以及C ++产生的开销只会分散他们解决实际问题的注意力。
效率低下的抽象编程模型已经过去两年了,您会注意到某种抽象不是很有效,但是现在您的所有代码都依赖于周围所有漂亮的对象模型,并且如果不重写应用程序就无法修复它。莱纳斯·托瓦尔兹(Linus Torvalds)
其他人则倾向于以更加平衡的方式看待它,既接受优点也接受缺点。
C可以使您轻松射击自己的脚;C ++使它变得更难,但是当您这样做时,它会使您全神贯注。-比尼亚(Bjarne Stroustrup)
您需要看看硬币的另一面:C ++。
在OOP中,我们想到一个抽象对象(并相应地设计程序),例如可以停下,加速,向左或向右转的汽车等。具有大量功能的结构根本不符合该概念。
例如,对于“真实”对象,我们需要隐藏成员,或者我们也可以通过真实的“是”关系进行继承,等等。
阅读下面的评论之后: 没错,(几乎)一切都可以用C来完成(这始终是对的),但是乍一看,我认为将c与c ++分开的是设计程序时的思维方式。
唯一真正改变的是编译器强加策略。即纯虚函数,等等。但是这个答案仅涉及技术问题,但是我认为主要区别(如上所述)是您在编写代码时的原始思维方式,因为C ++为您提供了一种更好的内置语法来执行此类操作,而不是在在C中有点笨拙
面向对象既指架构模式(甚至是元模式),也指具有帮助使用该模式实现或强制执行的功能的语言。
您可以实现“ OO”设计(Gnome桌面也许是用纯C语言完成OO的最佳示例),我什至已经看到了用COBOL做到这一点!
但是能够实现OO设计并不能使语言成为OO。纯粹主义者认为Java和C ++并不是真正的面向对象,因为您不能覆盖或继承基本的“类型”,例如“ int”和“ char”,并且Java不支持多重继承。但是,由于它们是使用最广泛的OO语言,并且支持大多数范例,因此大多数获得酬劳以产生工作代码的“真正”程序员都将它们视为OO语言。
另一方面,C仅支持结构(COBOL,Pascal和许多其他过程语言也是如此),您可以争辩说支持多重继承,因为您可以对任何数据使用任何函数,但是大多数人将其视为错误而不是比功能。
让我们看一下OO的定义:
C不提供这三个。特别是,它不提供Messaging,这是最重要的消息。
static
)函数和数据成员的单独文件。
OO有很多关键因素,但重要的是大多数代码不知道对象内部是什么(他们看到表面接口,而不是实现),对象的状态是受管单元(即,当对象不再是对象时,其状态也是如此),并且当某些代码调用对象上的操作时,他们这样做时并不确切知道该操作是什么或所涉及的(它们所做的只是遵循一种模式来抛出在墙上的“消息”)。
C做封装就好了;无法看到结构定义的代码无法(合法地)窥视结构内部。您需要做的就是将这样的定义放在头文件中:
struct FooImpl;
typedef struct FooImpl *Foo;
当然,将需要一个构建Foo
s 的函数(即一个工厂),该函数应将一些工作委托给分配的对象本身(即通过“构造函数”方法),并且还需要一种方法再次处理该对象(同时通过其“析构函数”方法对其进行清理)的细节。
方法分配(即消息传递)也可以通过以下约定完成:结构的第一个元素实际上是指向具有功能指针的结构的指针,并且这些功能指针中的每个必须以a Foo
作为其第一个参数。然后,调度就变成了查找该函数并使用正确的参数重写对其进行调用的问题,而使用宏和一点狡猾就不那么难了。(该函数表是使用C ++之类的类的真正核心。)
此外,这也给您带来了后期绑定:所有调度代码都知道,它正在将特定偏移量调用到对象所指向的表中。只需在对象的分配和初始化期间进行设置。可以使用更复杂的调度方案,这些方案可以为您带来更多的运行时动态性(以速度为代价),但是它们是基本机制之上的樱桃。
但这并不意味着C是一种OO语言。关键是C让您自己完成编写约定和调度机制的所有棘手工作(或使用第三方库)。这是很多工作。它还不提供语法或语义支持,因此实现完整的类系统(如继承)将不必要地痛苦;如果您要处理一个由OO模型很好描述的复杂问题,则OO语言对于编写解决方案将非常有用。额外的复杂性是合理的。
我认为C是完全正常和体面实现面向对象的概念,耸肩。从我实际的角度来看,我认为面向对象的语言的公分母子集之间的大多数差异本质上都是次要的和句法上的。
让我们从信息隐藏开始。在C语言中,我们可以通过简单地隐藏结构的定义并通过不透明的指针使用它来实现。当我们使用类时,这有效地建模了数据字段的public
vs. private
区别。它很容易做到,而且几乎没有惯用法,因为标准C库在很大程度上依赖于此来实现信息隐藏。
当然,您会失去使用不透明类型轻松精确地控制结构在内存中分配位置的能力,但这只是C与C ++之间的一个值得注意的区别。C ++在比较它在C上仍然可以保持对内存布局的控制能力时,比较它在C上面向对象的概念的能力时,绝对是一种出色的工具,但这并不一定意味着Java或C#在这方面要优于C,因为这两个使您完全失去了控制对象在内存中分配位置的能力。
而且我们确实必须使用类似的语法fopen(file, ...); fclose(file);
,而不是file.open(...); file.close();
大声疾呼。谁真正在乎?也许只是那些非常依赖IDE中自动完成功能的人。我确实承认,从实际的角度来看,这可能是一项非常有用的功能,但可能没有必要讨论一种语言是否适合OOP。
我们确实缺乏有效实施protected
领域的能力。我将完全在那里提交。但是我认为没有一条具体的规则说:“ 所有的OO语言都应该具有允许子类访问基本类的成员的功能,而普通客户仍然不能访问这些基类的成员。” 此外,我很少看到受保护成员的用例,这些用例至少一点也不怀疑会成为维护障碍。
当然,我们以“模仿” OO多态性与函数指针表和指向他们多一点的样板动态分配,以初始化这些类比vtables
和vptrs
,但样板的一点点从来没有给我带来太多的悲痛。
继承的方式大致相同。我们可以通过组合轻松地对其进行建模,并且在编译器的内部工作中可以归结为同一件事。当然,如果我们要向下转换,我们会失去类型安全性,并且我想说的是,如果您完全想要向下转换,请不要使用C,因为人们在C中所做的模仿向下转换的事情可能会因类型而变得可怕安全的观点,但我希望人们一点也不沮丧。类型安全性是您很容易在C语言中错过的东西,因为编译器提供了太多的空间来将其仅解释为位和字节,从而牺牲了在编译时捕获潜在错误的能力,但是某些语言被认为是面向对象的甚至不是静态输入的。
所以不知道,我认为很好。当然,我不会使用C尝试创建符合SOLID原则的大规模代码库,但这不一定是由于其在面向对象方面的缺点。如果我尝试使用C来实现此目的,我会遗漏的许多功能都与语言功能有关,这些语言功能没有直接被视为OOP的先决条件,例如强类型安全性,在对象超出范围时会自动调用的析构函数,运算符重载,模板/泛型和异常处理。就在我缺少C ++的那些辅助功能时。
foo_create
和foo_destroy
和foo_clone
,如
struct Foo
在这种情况下,我们需要保持不变的是确保外界无法访问和修改其数据成员,而这种不透明类型(已声明但未定义给外部世界)会立即给出。可以说,它是一种更强大的信息隐藏形式,与pimpl
C ++ 中的a形式相当。