面向对象编程比过程编程有什么好处?


77

我试图了解过程语言(例如C)和面向对象的语言(例如C ++)之间的区别。我从未使用过C ++,但我一直在和朋友们讨论如何区分两者。

有人告诉我C ++具有面向对象的概念以及定义变量的公共模式和私有模式:C语言没有。在Visual Basic.NET中开发程序时,我从来不需要使用它们:这些有什么好处?

我还被告知,如果变量是公共变量,则可以在任何地方访问它,但是尚不清楚它与C之类的语言中的全局变量有何不同。还不清楚私有变量与局部变量有何不同。

我听到的另一件事是,出于安全原因,如果需要访问某个函数,则应首先继承该函数。用例是管理员应该只拥有所需的权限,而不是所有的权限,但是似乎有条件的也可以工作:

if ( login == "admin") {
    // invoke the function
}

为什么这不理想?

鉴于似乎有一种方法性的方法可以完成所有面向对象的编程,所以我为什么要关心面向对象的编程?



11
此编程范例图可能会对您有所帮助。
AProgrammer

26
+1以抵消某些反对票。如果一个同事问我这样的问题,我可能会有些顾虑,甚至会否决他(假设他旁边有向下箭头)。但是,这个问题似乎是由未来的软件工程师提出的,听起来他在发布之前花了一些时间思考和讨论该主题。我投票赞成帮助他而不是解雇他。
DXM

14
@DXM好主意!在同事周围漂浮的向下投票/向上投票箭头...这会产生奇迹。
yannis 2011年

2
标准的计数器参数:还有一种汇编方法可以完成您在C中可以执行的所有操作,那么为什么还要关心C?(提示:这完全是为了提高抽象水平。C++设法做到了这一点,而并没有牺牲C的大部分速度
。IMO

Answers:


135

到目前为止,所有答案都集中于您所陈述的问题主题,即“ c和c ++有什么区别”。实际上,听起来您知道区别是什么,您只是不明白为什么需要该区别。因此,其他答案试图解释OO和封装。

我想提出另一个答案,因为根据您的问题的详细信息,我认为您需要退后几步。

您不了解C ++或OO的目的,因为在您看来,您的应用程序似乎只需要存储数据。该数据存储在变量中。“为什么要使变量不可访问?现在我不能再访问它了!通过将所有内容公开,或者更好的是全局的,我可以从任何地方读取数据,而且没有问题。” -是的,根据您当前正在编写的项目的规模,可能没有很多问题(或者有很多问题,但是您还没有意识到这些问题)。

我认为您真正需要回答的基本问题是:“为什么我要隐藏数据?如果这样做,我将无法使用它!” 这就是为什么:

假设您开始一个新项目,打开文本编辑器并开始编写函数。每次您需要存储某些内容(以备以后使用)时,都将创建一个变量。为简化起见,您将变量设为全局变量。您的应用程序的第一个版本运行良好。现在,您开始添加更多功能。您具有更多功能,需要从新代码中读取之前存储的某些数据。其他变量需要修改。您继续编写更多功能。您可能已经注意到的(或者,如果没有,您将来肯定会注意到的)是,随着代码的变大,添加下一个功能所需的时间越来越长。随着代码的增大,在不破坏以前有用的功能的情况下添加功能变得越来越难。为什么?因为你需要记住所有您的全局变量正在存储,您需要记住所有变量都在何处被修改。而且您需要记住可以按确切的顺序调用哪个函数,如果以不同的顺序调用它们,则可能会出错,因为全局变量还不是很有效。你遇到过这个吗?

您的典型项目有多大(代码行)?现在,对一个项目进行成像是您的5000到50000倍。另外,其中有多个人在工作。团队中的每个人如何记起(甚至意识到)所有这些变量在做什么?

我上面描述的是完美耦合代码的示例。从时间的曙光开始(假设时间从1970年1月1日开始),人类一直在寻找避免这些问题的方法。避免它们的方法是将代码分成系统,子系统和组件,并限制有多少个函数可以访问任何数据。如果我有5个整数和一个表示某种状态的字符串,那么仅5个函数设置/获取值对我来说会更容易处理这种状态吗?还是如果100个功能设置/获得了相同的值?即使没有OO语言(例如C),人们也一直在努力将数据与其他数据隔离,并在代码的不同部分之间创建清晰的分隔边界。当项目达到一定大小时,编程变得不那么容易,“我可以从函数Y中访问变量X”,

这就是为什么引入OO概念的原因,也是它们如此强大的原因。它们使您可以对自己隐藏数据,并且您想要有目的地这样做,因为看到该数据的代码越少,则添加下一个功能时,您将破坏某些东西的机会就越少。这是封装和OO编程概念的主要目的。它们使您可以将我们的系统/子系统分解为更精细的框,以至于无论整个项目有多大,给定的一组变量只能由50-200行代码访问,仅此而已!OO编程显然还有很多,但是,从本质上讲,这就是C ++为您提供将数据/函数声明为私有,受保护或公共的选项的原因。

OO中的第二大想法是抽象层的概念。尽管过程语言也可以具有抽象,但是在C中,程序员必须做出有意识的努力来创建这样的层,但是在C ++中,当声明一个类时,会自动创建一个抽象层(是否由抽象来决定还是要由您自己决定)将添加或删除值)。您应该阅读/研究有关抽象层的更多信息,如果您还有其他问题,我相信这个论坛也将很乐意回答这些问题。


5
很好的答案,似乎已达到问题的适当水平
Carlos

29
+1 ...主要是针对“从时间的曙光开始(假设时间从1970年1月1日开始)...”开始
CaffGeek

4
@Chad-我一直认为单行应该给我至少得分:)
DXM

有一种方法可以解决您在过程范式中讨论的规模问题。这就是所谓的功能。但是解释问题的好方法。
annoying_squid

@DXM-我不确定我是否正确理解答案。我们也可以在过程编程中实现相同的设置/获取功能。我们可以在C语言中编写set / get函数来修改/获取全局变量。同样使用这种方法,我们限制了修改全局变量的函数的数量。即使在OOP中,如果我们也使用set / get方法,我们将在对象外部使用这些方法来更改值。
kadina

10

嗯...也许最好备份并尝试给出一些有关面向对象编程的基本意图的想法。面向对象编程的大部分意图是允许创建抽象数据类型。对于一个您无疑熟悉的非常简单的示例,请考虑一个字符串。字符串通常会有一个缓冲区来保存字符串的内容,一些可以对字符串进行操作的功能(搜索,访问字符串的一部分,创建子字符串等)。它(至少通常)还有一些要跟踪字符串的(当前)长度和(大概)缓冲区的大小,因此,例如(如果)将字符串的大小从1增加到1000000,它将知道何时需要更多的内存来容纳更大的字符串内容。

这些变量(缓冲区,当前长度和缓冲区大小)是字符串本身的私有变量,但它们不是特定函数的局部变量。每个字符串的内容都有特定的长度,因此我们需要跟踪该字符串的内容/长度。相反,相同的函数(例如,提取子字符串)可能在不同的时间对许多不同的字符串进行操作,因此数据不能位于单个函数的本地。

这样,我们最终得到一些字符串专有的数据,因此字符串函数只能(直接)访问它们。外部世界可以使用字符串函数来获取字符串的长度,但无需了解任何有关字符串内部的知识即可。同样,它可能会修改字符串-但同样,它是通过字符串函数来完成的,只有它们直接修改字符串对象本地的那些变量。

就安全性而言,我要指出的是,虽然这是合理的,但实际上并非如此。特别是,C ++中的访问专门旨在满足与操作系统中访问相同的要求。操作系统应该强制执行这些限制,因此(例如)普通用户不能执行为管理员保留的操作。相比之下,C ++中的访问控制仅用于防止意外。通过设计,任何想要绕过它们的人都可以轻松绕过它们。它们与将文件标记为只读的顺序相同,因此您不会意外删除它。如果决定删除文件,将其从只读更改为可读写很简单;将其设置为只读的所有方法至少可以使您考虑一秒钟,然后决定删除该文件,这样就不会因在错误的时间按了错误的键而意外删除该文件。


6

OOP与C的关系并不是您讨论的任何事情。这主要是关于将代码打包到不会/不会(有时甚至是故意)相互影响的区域中。

C使您基本上可以从任何地方运行任何功能。OOP通过将方法分组到类中并仅允许您通过引用包含它们的类来使用这些方法来防止这种情况。因此,OOP的一大潜在优势是,您无需经过大量经验告诉您应该进行更好的代码安排就可以了。


4
-1。C中没有什么可以使所有功能变为全局的。您可以声明任何静态函数,从而将其范围限制为本地文件。在这方面,C与C ++,Java等没有区别。同样,OOP与语言语法无关,您可以用C编写OO程序,尽管它们比支持OO的语言更粗糙。相反:您不会因为选择支持OO的语言而获得OOP。面向对象是一种编程风格,而不是一种语言功能。

@Lundin:虽然您在技术上是正确的,但是您已经错过了重点。OOP语言使其默认行为以OOP方式表现。C没有。
约翰·费希尔

1
OO语言中没有什么强迫您执行此操作。例如,我看到了无数晦涩的C ++程序,而没有任何值得一提的OO。同样,如果您对OO没有任何了解,但是尝试实现类,传统等,那么大概有100%的机会创建混乱的程序。

@Lundin:我认为C ++不是一个很好的例子。它的意思是(或至少是打算)无需(很多)修改就可以编译C程序。在顶部添加类并不能使它成为C#或Java级别的OOP语言,但确实可以进行这种开发。
约翰·费舍尔

您也可以用Java编写非OO程序,只需在一个巨大的主文件中砍掉……OO 仍然不是特定于语言的,如果程序员不了解OO,那么世界上没有语言会保存它们。

4

编写良好的课程应该是一点“信任之岛”:您可以使用它,并假设它做了“正确的事”,并且使您免受常见陷阱的困扰。这使一个好的课程成为一个构建块,可以作为一堆函数和变量重用,虽然它们可以很好地工作,但是向您展示了所有丑陋的内脏,并迫使您了解它们如何协同工作,如何初始化它们。好的类别应该像USB插头,而过程解决方案就像一堆电线,芯片,锡和焊接头。

没有深入讨论的一点是接口/实现方面。接口描述行为,但不描述实现。所以列表界面描述了这个概念列表及其行为:您会期望诸如add,remove和size方法之类的东西。现在,有很多不同的方法可以实现此列表,例如,作为链接列表或使用数组缓冲区。OO编程的强大之处在于,通过使用接口,您可以在不了解实现的情况下推断出行为。访问内部变量或方法会破坏这种抽象,您不能将一个列表实现替换为另一个列表实现,并且如果不使用类来触摸代码,也无法改进现有的实现。这是需要专用变量和方法的主要原因之一:为了保护实现的内部细节,因此抽象保持完整。

OO向前迈进了一步:例如,对于库,您可以为尚不存在的事物定义一个接口,并编写适用于该接口的代码。用户可以编写实现该接口的类,并使用该库提供的服务。这提供了过程编程无法实现的一定程度的灵活性。


接口的概念并不是面向对象语言所独有的。我认为,更大的因素是,在非OOP语言中,模块中使用的几乎所有功能都必须属于同一全局名称空间。这就要求要么使用前缀函数名称来指示其作用,要么使用许多听起来相似的方法来做完全不同的事情(例如,SetLocation可能用于移动Monster,而SetPosition可能移动PopupWindow,并且Move可能用于调整位置的DisplayCursor)。试图找到正确的“移动”方法...
supercat

如果编写MyMonstor->一个编辑器时只显示适用于type的方法的列表,则...将变得更加容易Monster。如果有许多种不同的事物,每种事物都支持大约十二种操作,则将方法列表中的混乱量减少90%可以极大地降低生产率。
超级猫

@supercat名称冲突是一种语言问题,而不是非OOP问题。另一方面,命名空间是有问题的,因为编译器本质上需要自动重命名该函数或对其进行处理。那么,为什么不手动进行呢?
annoying_squid

@annoying_squid:什么OOP提供的是有效利用的能力类型一个功能可按的主要参数的选择命名空间。如果我有一个it类型的变量SuperFancyWhizBang,调用SuperFancyWhizBang的方法之一就it不需要写出类型SuperFancyWhizBang; 说it.woozle()直接将编译器自动寻找woozleSuperFancyWhizBang
超级猫

3

有一种方法可以用Turing机器完成所有工作,或者至少使用汇编语言来完成C或C ++程序最终会编译成的机器代码。

因此,区别不在于代码可以做什么,而在于人们可以做什么。

人们会犯错误。很多。

OOP引入了一种范式和一种语法,可帮助减少可能的人类编码错误的空间大小和概率密度。有时,通过使该错误对于某类数据对象非法(例如,不是为该对象声明的方法)。有时,与标准用法相比,使错误更冗长,或在外观上显得古怪。有时,通过要求接口使用尽可能少的不一致或纠缠的用法(公共与私有)。等等

项目越大,出错的可能性越高。如果仅使用小型程序,可能不会接触到新的编码器。因此,对于OOP为何有价值的潜在困惑。


2

您的问题似乎更多是关于OOP的目的,而不是区别。您帖子中的概念是封装;并且存在封装以支持CHANGE。当其他类访问您的内部时,很难在不破坏它们的情况下对其进行修改。在OOP中,您提供一个接口(公共成员),通过该接口,您可以允许其他类与您的类进行交互,并且您可以隐藏内部结构,以便可以安全地对其进行更改。


只需使用函数原型。那就是封装。
annoying_squid

2

无论我在哪里读取私有变量都无法访问,而可以访问公共变量,那么为什么不将public公开为全局,将private公开为局部呢?公共和私人的真正用途是什么?请不要说每个人都可以使用它,我想我们为什么不使用某些条件进行通话呢?

希望您在应用程序中不要多个字符串。我也希望您的局部变量在函数调用之间保持不变。这些方面在可访问性方面可能是相同的,但在生存期和其他用途方面却是相同的?他们绝对不一样。


1

正如许多人所说,一旦编译,任何程序都将变成二进制代码,并且由于二进制字符串可能用来表示整数,因此任何程序最终都只是一个数字。但是,定义所需的数字可能非常困难,这就是为什么出现高级编程语言的原因。编程语言只是它们最终产生的汇编代码的模型。我想通过这篇关于面向上下文的编程的很好的文章来解释一下过程编程和OO编程之间的区别http://www.jot.fm/issues/issue_2008_03/article4/

如您从这张图中描绘的照片中所看到的,过程编程仅提供一个维来将计算单元与名称相关联。在这里,过程调用或名称直接映射到过程实现。在图a中,调用m1只能选择调用过程m1的唯一实现。

面向对象的程序设计为过程程序设计增加了名称解析的另一个维度。除了方法或过程名称外,在查找方法时,消息分发还考虑了消息接收者。在图b中,我们看到方法m1的两种实现。选择合适的方法不仅取决于消息名称m1,还取决于实际消息的接收者(此处为Ry)。

这确实允许封装和模块化。

在此处输入图片说明

Figure-c最后是关于面向主题的编程的又一个方面,它扩展了面向对象的方法调度。

希望这可以帮助您从不同的角度思考OOP。


0

(+1)问一个您不了解的事物的问题,即使听起来很傻,它的好处。

区别在于面向对象和类的编程。“普通C”,可处理数据和功能。“ C ++”添加了“对象和类”概念,以及几个相关的辅助概念。

但是,我建议开发人员在“ C ++”之前学习“ Plain C”。或在“对象Pascal”之前的“过程Pascal”。

许多开发人员认为,学生应该只教一门课程。

例如,老教师没有OO,只教“普通结构C”。

或者,只教OO而不是“普通C”的“时髦”老师,因为“你不需要它”。或两者兼而有之,而不在乎教学顺序。

我宁愿认为,应该同时向学生教授“结构化纯C”和“面向对象的C(C ++)”。首先使用“普通C”,然后使用“ C ++”。

在现实世界中,您需要学习两种范例(以及其他范例,例如“功能”)。

将结构化程序视为一个巨大的单例“对象”可能会有所帮助。

您还应该强调名称空间(“模块”),在两种语言中,许多老师只是忽略它,但是,它很重要。


命名空间和重载只会使程序更难以理解。它使识别过程上下文敏感。当您foo()在C ++中看到它时,它可以是全局函数,当前名称空间中的函数,与with一起使用的名称空间中的函数using,方法,继承的方法以及是否在函数调用中:它可以位于以下名称空间中:可以通过基于参数的名称查找来解决,Java和C#也是如此。在C语言中,它只能是当前源文件中的静态函数,也可以是标头中的一个静态函数。
Calmarius 2012年

到处都写MODULE_Foo()可能看起来很混乱,但是至少您完全知道它是哪个功能。
Calmarius 2012年

@Calmarius的解决方案显然是不给任何名称foo命名。

0

一言以蔽之,就是项目管理。我的意思是C ++可以帮助我制定有关他人如何使用我的代码的规则。在一个550万行的项目中,我发现面向对象的编程非常有帮助。另一个优点是编译器使我(和其他所有人)遵循某些规则,并在编译时捕获较小的错误。所有理论上的优势也都存在,但是我只想专注于日常实践。毕竟,所有这些都可以编译成机器代码。


-1

面向对象编程是带有框的过程编程。

在PP中,您有一个框,一种状态,随着项目的增长,它会变得非常大,每当您忘记那个大状态的一小部分时,就会出现副作用。

在OO中,您有很多盒子,很多州,并且随着项目的发展,盒子会稍微增长,盒子的数量也会增长很多。

看到较小的盒子仍然很容易,容易看到整个画面,但实际上几乎是不可能的,因为查看类和接口隐藏了可能具有重要影响的实现细节。

在函数式编程中,您有许多功能框,并确定每个函数都具有一个入口(参数)和一个出口(返回),而严格来说,没有其他访问外部上下文的权限。

由于没有状态,也没有副作用(通过设计),因此您可以安全地从整体上分析任何功能,并知道在任何情况下100%的性能。

因为您是按表示动作的逻辑单元对代码进行装箱,所以每个典型动作也只能有一个方框。

与OOP相比,这将使任何大型项目的代码缩水很多,而OOP则可以在整个代码库中的不同类中隐藏多个模拟功能。

这也将远远超过PP,因为您无需再跟踪XXXXXXXL的状态,就可以使项目增长更长的时间。

总之,PP可能是处理简单程序的最简单方法,而FP可能是处理复杂程序的最简单方法。

如果您考虑到统一所有代码库和提高代码重用性的目标,则应始终使用FP,因为FP是唯一在大规模上有意义的范例,并且是唯一具有100%可重用性的范例(您可以只需复制粘贴函数并在其他地方使用它,就不会产生任何开销)。

您将免费获得100%可靠的单元测试。

而且您不必编写“ private static final of_doom genius awesome string_1”。

您可以免费获得并行性。


-3

一个简单的句子区别就是C ++是带有类的C。(尽管现在更多),我不解释为什么您不想通过阅读Wikipedia上一篇有关C ++的精彩文章来了解两者之间的区别。 。这篇文章将极大地帮助您:-C ++(Wikipedia)

在该问题上使用谷歌搜索也会有所帮助。让随机的人解释这可能很棘手。恕我直言,通过阅读比问别人更能理解


我读过这些,但他们只是说了在哪里使用,但仍然没有解决我的问题。
niko

我一直不遗余力地问这个问题,但仍然无法理解区别,所以我遇到了stackoverflow来帮助我解决这些问题
niko

1
@niko,您在这里误解我了。我的意思是,您应该尝试通过阅读来理解两者之间的区别。问你的朋友不好。因为他们会提供自己的理解,可能无法满足您的需求。但是,不用担心,这里有很多同龄人,他们一定会为您提供帮助的:-)
Pankaj Upadhyay

2
我没有拒绝投票,但是对“ C ++是带有类的C”感到非常诱惑。我们当中那些实际上可以进行C ++工作的人会竭尽全力地试图使人们摆脱困境
DeadMG

1
@Pankaj:是的。它带有类的C语言。绝对不再是带有Classes的C语言了,将其称为已经过了将近30年的历史了。从那时起,C ++已经走了很长一段路。现在,使用C ++进行编程的人永远不会那样引用它。它会鼓励最坏的习惯和错误的印象。
DeadMG
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.