我们是否应该避免C ++具有但Java没有的语言功能?


110

假设我只能在项目环境中使用C ++。防止使用C ++拥有但Java没有的某些语言功能是否很好(例如:多重继承,运算符重载)?

我认为原因是:

  1. 由于Java比C ++更新,因此如果Java没有提供C ++具有的功能,则意味着该功能不好,因此我们应避免使用它。
  2. 具有C ++特定功能(例如:朋友功能,多重继承)的C ++代码只能由C ++程序员维护或检查,但是如果我们只编写Java之类的C ++(没有C ++语言特定的功能),则两者都可以维护或检查代码。 C ++和Java程序员。
  3. 有一天可能会要求您将代码转换为Java
  4. 没有C ++特定功能的代码通常更易于维护
  5. 每个C ++语言特定的功能(例如:多重继承)都应具有在Java中实现的替代方法。如果不是这样,则意味着设计模式或代码体系结构存在问题。

真的吗?


7
如果您查看您的建议,则是在谈论创建编码标准。您正在创建和遵循标准的事实实际上将提高可维护性。
布兰丁

63
Java代码是否也应限于使用C ++语言才能找到的功能?因此,例如volatile,不要使用Java ,包私有的类成员访问,反射/自省,finally-blocks,已检查的异常等等?整个问题那种没有多大意义...... C ++和Java是表面上相似,但最终还是不同的语言。
海德

5
如果人们不愿意使用新功能,语言将如何发展?这些新功能的目的是通过解决常见问题来使您的生活更轻松。如果用户不使用新功能,那么任何人都没有实现它们的动力。
马修

72
如果需要Java,请使用Java。如果您使用C ++,请使用C ++,而不要使用C ++语法对Java进行一些可怕的扭曲模仿。使用C ++但将自己限制为Java的功能集会使您两全其美。
杰里·科芬

11
您会惊讶于差异会以多快的速度咬住您(并且很难)。 在Java和C ++中SomeObject a = previousObject;非常不同的事情。在Java中,它复制引用,而在C ++中,它复制对象。在Java中,对的修改a也会影响先前的对象。在C ++中,您有两个单独的对象。
Clockwork-Muse

Answers:


307

不。这是严重而错误的指导。

  • Java功能在某种程度上并不比C ++功能好,尤其是在真空环境中。
  • 如果您的程序员不知道如何使用功能,请培训或雇用更好的开发人员;将开发人员限制在团队中最糟糕的位置,是失去优秀开发人员的快速简便的方法。
  • YAGNI。今天解决您的实际问题,而不是解决某些将来的问题。

6
最近有一个问题是,开发人员使用他们通常不编写的语言来审查代码。我认为这里的智慧适用:优秀的开发人员几乎可以在任何一种语言中发现许多基本错误,但是每种语言都有很多问题,以至于使用语言开发人员进行审阅时都可以最大程度地受益。如果您的C ++是“仅Java功能”,则甚至是这样。
corsiKa '16

5
+1除了第二点。OP应该花时间阅读有关“ C ++最佳实践”的知识,以决定应使用哪些功能。关于C ++的最新信息是,朋友功能多重继承被视为“ 不良实践”。喜欢的东西模板操作符重载,如果你明智地使用它们是恕我直言良好做法。
some_coder '16

4
@some_coder:全都知道何时何地。当我执行基于策略的编程时,我从(可能是多个)策略类继承来用这些策略类需要起作用的成员来扩展宿主类的结构。这就是它的工作方式。另外,operator<<( ostream &, T const & )通常是通过实现的friend。这是有关什么是良好的语言功能毯语句,什么是不好的一个问题:他们是很好的意见,当他们不只是...
DevSolar

142

仅仅因为语法在表面上看起来相似,并不意味着这两种语言是兼容的。

1、4和5确实是同一问题:

现在,我不喜欢C ++,但是说“没有C ++特定功能的代码通常更易于维护”简直太荒谬了-您真的相信Java做到了一切正确,并且在忽略了所有不良特性的情况下采用了所有优良特性吗?您是否真的相信普遍存在“坏”或“好”功能?如果有,为什么我们没有一种纯粹的语言呢?不,Java当然不是那种语言。这是否意味着Java和C ++没有用?当然不是。

如果您的领导者决定您将移植到C#而不是Java,该怎么办?C#不仅支持重写运算符,而且它也是默认的-如果您要要求人们使用例如obj.Equals(obj2)而不是obj == obj2,人们将一直在犯错误。即使只保留两种语言的共同特征,也有不同的期望和不同的文化。如果您if (myField == null)在C ++中做类似的事情,人们会立即发现您是新手。如果您if (null == myField)在C#中使用,人们会发现您不是真正的C#本机-C开发人员学会使用“翻转”变体的原因在C#中不再存在。

使用您的逻辑,我们应该坚持使用机器代码或汇编或COBOL,因为为什么要更改为Pascal之类的东西,而只是添加程序员必须学习的新功能?当它甚至没有循环时,为什么还要使用像SQL这样的东西呢?当SQL没有循环而X却有循环时,为什么还要使用SQL以外的其他方式呢?

Java程序员肯定不能维护C ++代码。我不明白您的主意是什么-将C ++限制为仅与Java完全相同的功能时,还剩下什么?您甚至都不会获得方法调用-甚至不会得到函数调用。同样,仅因为两种语言都使用花括号,并不意味着这些语言可以以任何方式互换。

无论您做什么,转换类似Java的C ++代码都极容易出错。差异太多了。如果您需要用另一种语言重写应用程序,请考虑采用合理的方法对所有内容进行模块化,以便可以在不破坏整体的情况下更换部件。但是最后,YAGNI –不管您做什么,都要使代码“准备好转换为Java”要付出巨大的代价。那时候最好在增加或改进功能上花费更多时间。

我们使用不同的语言,因为它们为我们提供了解决问题的不同工具。如果您需要在任何地方都能运行的可执行文件,请使用Java。如果您希望代码“无处不在”进行编译,则C ++可以正常工作。如果您想要易于理解和解析的代码,请使用LISP或其他工具。但是我可以告诉你一件事-用一种语言编写代码就像使用另一种语言编写代码始终是一个错误,并且您会遭受痛苦。更不用说当您实际雇用C ++的人时,他将运行第二个,他会看到“与Java兼容”的代码。而且... Java家伙也会做同样的事情。您知道,即使“知道” C ++和Java,我也会像地狱般奔跑:)

实际上,我实际上不得不处理一个Pascal开发人员编写的(普通的)C代码,该开发人员似乎觉得自己与您一样。他使用#defines重新定义了C,使其看起来和感觉更像Pascal,并附带了“ BEGIN转换为{”之类的内容。结果是相当可预测的-C和Pascal开发人员都无法理解的代码,以及由于C之上Pascal的“抽象”泄漏而导致的大量错误。从今天的角度来看,Pascal和C 几乎相同。即使使用C <-> C ++也有很大的不同,而且对于诸如C ++ <-> Java之类的东西来说,这仍然是小菜一碟。


9
“如果您想要易于理解和解析的代码,请使用LISP或其他工具。” 我不同意这一点。Lisp解析起来很简单,但是正因为如此-因为它是在解析器的知识和有效地构建它们的原理还处于起步阶段的时候创建的,所以他们猛烈抨击并采用了最荒谬的简单化方法这可能会起作用-您不会从现代语言中获得很多语法上的好处。因此,它很容易解析,但一点也不容易理解。人们称其为“丢在多余的括号中”是有原因的。
梅森惠勒

9
@MasonWheeler这是一个趣味问题-C程序员称其为LISPers:P而不是LISPers:P。计算括号-与典型的C ++程序一样多。真正的区别是他们的位置(myFunc()vs (myFunc)),这是有充分理由的。基于LISP的语言仍然非常流行,特别是在数学/物理学界。最主要的是,LISPy语言对现代C风格的程序员非常陌生 -但这并不是忽略它的理由。Scheme,Clojure和某种程度上基于ML的语言(OCaml,F#...)确实非常轻便。
a安

4
@Luaan我可以肯定地说LISP在物理上并不流行。该领域的两种主要语言是FORTRAN和C ++。FORTRAN之所以“受欢迎”,是因为其中写了很多旧代码,但是几乎所有新程序都是用C ++编写的。作为研究物理学家,我从未遇到甚至从未听说过LISP计划。并不是说它们不存在,但是这些语言绝对不受欢迎
James Matta

4
@Luaan我们可能会或可能不需要更多的软件开发人员。我们绝对不需要更多的CS毕业生。这就像在说:“我们需要更多的结构工程师,所以我们需要更多的理论物理学毕业生。”
Miles Rout

4
@ user1717828这是为了避免if (myField == null)像as那样令人迷惑的效果if (myField = null),后者会编译出很好的C / C ++,但可能并没有达到您的预期。另一方面,if (null = myField)由于您无法分配给常量,因此会引发错误。
沃林

94

我会按顺序回答您的问题。

  1. 如果Java没有提供C ++所具有的功能,则意味着该功能不好,因此我们应避免使用它。

是的,Java中不存在的任何功能都是硬盘驱动器上的垃圾。它必须从您的代码库中刻录。那些不听话的人会被狼吞虎咽,他们的灵魂被用来安抚RAID之神。

  1. 具有C ++特定功能(例如:朋友功能,多重继承)的C ++代码只能由C ++程序员维护或检查,但是如果我们只编写Java之类的C ++(没有C ++语言特定的功能),则两者都可以维护或检查代码。 C ++和Java程序员。

如有疑问,请为团队中最不称职的成员编写代码。读取代码的频率远高于编写代码的频率,而您可以编写的聪明代码则太聪明了,无法读取。您的前台工作人员无法理解的代码审查应被拒绝。要提供帮助,请教他们如何在周末使用Visual Basic 2.0编程,然后以您使用的任何语言来模拟他们的编码样式。

  1. 有一天可能会要求您将代码转换为Java

真正!但是为什么停在Java上呢?您可能需要一天将代码转换为基本代码,汇编程序和/或perl。由于存在使用每种语言的perl解释器,因此只需将您的程序编写为长的perl字符串,然后以选择的语言将参数编组到其中即可。

现在,当您需要更改语言时,只需重写包装perl字符串的代码即可。

  1. 没有C ++特定功能的代码通常更易于维护

确实,使用较少功能的代码更易于维护。并且由于所有语言都等效于Turing Machine,并且Turing Machine具有所有编程语言中最少的功能,因此让上述perl解释器实际运行可解决您问题的Turing Machine(磁带和全部)。

  1. 每个C ++语言特定的功能(例如:多重继承)都应具有在Java中实现的替代方法。如果不是这样,则意味着设计模式或代码体系结构存在问题。

实际上,C ++语言特定的功能具有重要的用途。因为它们不是Java,所以它们是恶心的,并且使用Java的人可能被称为异端。如果C ++没有这些语言功能,那么您将找不到需要牺牲的语言。C ++语言功能解决了问题!


看起来,C ++和Java具有不同的功能集。在C ++和Java的交集下进行编程,所产生的代码已放弃了这两种语言的大多数优势。

Java的部分开发是对某些C ++功能滥用的反应。这并不意味着该反应是合理的,尤其是几年之后,这些功能已经成熟。

您可以通过运算符重载来做可怕的事情;但是没有一种语言可以阻止用这种语言编写可怕的代码,除非它阻止了所有用该语言编写的代码。

您还可以通过运算符重载来做非常优雅的事情。简单的事物,例如复数或矩阵类,其工作方式类似于复数或矩阵。

应谨慎使用Java中不可用的C ++功能。仅仅因为您当前的开发人员在其当前的技能水平上不了解某个功能,并不意味着该功能永远不应该被使用;同时,仅仅是因为您可以使用功能,并不意味着您应该这样做。但是,在拥有大量Java的商店中,抵抗非Java风格的功能的抵抗力要强于它的抵抗力。

最好通过重写来完成语言之间的代码转换,无论它们在结构上如何相似。任何不进行此类重写的尝试都会惨遭失败。

许多C ++特定功能都是维护的奇迹。类型擦除(如std::function)使您可以取消层次结构的耦合,从而减少了依赖性。智能指针和确定性生存期以及RAII减少了运行时意外情况,并避免了资源模板化。大量的静态类型检查意味着代码无法编译,而不是无法运行。混入可减少代码重复。ADL允许类型层次结构之间接口的临时独立扩展。Lambda允许您在使用代码的旁边编写代码。转发和移动可减少副本,并使功能样式(无副作用)编程高效。运算符重载降低了线路噪音,并使代码看起来更像其正在建模的数学。

当心图灵焦油坑。您可以用任何语言(包括带有磁带的原始图灵机)实施所有操作,但这并不意味着您应该这样做。在C ++中使用Java风格的构造来模拟C ++功能是一个可怕的想法。它将导致无法维护的代码,任何人都无法阅读或理解。

您可以从Java设计中汲取灵感并将其移植到C ++。我非常喜欢Python语言功能并将其用C ++实现,因为我喜欢语法。但这并不意味着我将类方法编写为带有显式自我的静态方法,然后编写将非静态方法调用转发给它的包装器。

现代C ++与Java模拟和拒绝的语言完全不同。不要被一种语言的功能所束缚;没有“一种真正的语言”。了解新的语言及其功能,并吸收其独特性。


1
-1,long不清。
djechlin '16

4
-1论据,稻草人论证
user949300 '16

28
+1,正确解构询问的问题,我笑了:)

11
+1。有趣,但要点。非常适合实际阅读的人。
Luis Masuelli

14
“在C ++和Java的交集中进行编程所产生的代码已放弃了这两种语言的大多数优势。” 打钉子在头上!+1
Ajedi32 '16

56

我只想回答你的原因:

  1. 我不明白你是如何得出这个结论的。不同的语言具有不同的功能。取决于范围,语言结构,有时创建者的喜好以及更多原因。语言的某些功能可能不好,但是您的概括是错误的恕我直言。
  2. 像Java一样编写C ++可能会导致更糟糕的代码。例如,如今使用C ++ 11,我避免使用new / delete,但使用共享指针。在您的情况下,我将仅依赖于new / delete。如果您的程序员只懂Java,请培训他们或雇用更好的Java。
  3. 那可能会发生吗?您为什么不首先用Java编写?我认为用新语言从头开始重写程序通常不是一个好主意,并且您将需要一个很好的理由来证明这种风险。
  4. 这只是一个假设。
  5. 恕我直言高度依赖于您的方案或用例。

27
Java程序员也不会使用delete
圣保罗Ebermann

8
我不得不解决由Java程序员不了解而导致的内存泄漏delete。我记得,在至少其中一种情况下,new也不需要动态分配()。
伊桑·卡明斯基

16
@EthanKaminski是的,这就是您可以轻松发现C ++新手的方法,特别是那些来自Java背景或类似背景的人。别再用新东西了,该死!
a安

1
@PaŭloEbermann是的,甚至更糟。
西蒙(Simon)

1
“在您的情况下,我将仅依赖于new / delete”-有趣的是,我会想到,“仅具有Java(类似)功能”的C ++习惯用法将被专门使用make_shared
凯尔·斯特兰德

26

Java具有C ++所没有的功能,例如内置,快速,可靠的垃圾收集器,单根对象层次结构以及强大的自省功能。

Java的其他功能旨在与Java独有的功能配合使用,并且由于新功能弥补了Java的不足,许多Java遗漏了C ++功能也是可能的。例如,Java没有具有确定性析构函数的堆栈分配对象,但最终有了块(以及Java 7之后的try-with-resources)和垃圾收集器来弥补这一不足。

C ++最终没有块或垃圾回收。如果您不使用析构函数,因为它们在Java中不存在(我不算终结器),那么您就处于C级的资源管理(即所有手册),除了您甚至无法对清理进行分组进入您要转到的清理块,因为Java也没有goto。您是否真的认为这会改善可维护性?

Java具有一个总体的对象层次结构,其中每个类最终都派生自Object,并且少数基本类型也可以自动装箱到此类中。这样就可以一次编写对象容器,以保存指向对象的指针。Java 5引入了泛型,该泛型摆脱了此类容器必需的强制类型转换,但仍可以编译成几乎相同的代码。

C ++没有这样的层次结构。为了方便编写适用于多种类型的容器,可以使用模板,这些模板是编译器根据不同类型的需要实例化的蓝图。您会禁止使用模板(和宏),还是走C语言路线,要么一遍又一遍地为不同类型编写相同的容器代码,要么使用空指针?(等等,Java没有空指针!)确定要增加可维护性吗?

体面的语言具有旨在协同工作的多种功能。通过禁止语言A的功能(因为语言B没有这些功能),您正在削弱语言A,因为您无法同时从语言B中添加使B成为一个连贯整体的功能。

这并不是说您不能限制C ++允许的功能。C ++是一门古老的,历史悠久的语言,它强调使程序员能够在安全性和易用性方面做自己想做的事情,并且并非所有其灵活的功能对于构建良好的程序都是必需的。有许多编码标准限制了某些功能的使用。例如,Google的指南大多禁止多重继承,复杂的模板和例外(尽管最后一个是出于历史原因)。但是没有功能被禁止是“因为Java没有它”。仅在C ++框架内考虑了这些功能。


27
经验丰富的C ++老手通常会拒绝Google编码指南。恰恰是因为现代C ++ 使用了这些功能,所以它要好得多。是的,这使它与众不同。
Jan Hudec

17
请注意,C ++没有finally块,但它具有RAII,这在大多数情况下要好得多。而且,又一次不同。
Jan Hudec

7
Java Object几乎可以void*用于大多数用途-还是,哎呀。
昆汀

1
除了功能选择之外,还有样式和成语的问题。如果程序对编写语言使用正常的习惯用语,则通常更易于阅读和维护。即使仅使用类似Java的功能就可以编写C ++程序的一部分,结果也将是奇怪的,令人陶醉的C ++。
Patricia Shanahan

2
我会为正确的答案(“不要这样做”)表示赞同,但是我不能忍受Java一定程度上比C ++“先进”的基本前提。C ++ 不需要垃圾收集器或单根对象层次结构,就像Java不需要... err ...抱歉,我不知道如何完成这句话,因为我错过了Java中的确定性析构函数,并且finally不能替代它们。两种语言根本不同。
DevSolar

25

当您已经得出一个似乎完全合理的结论的数字时,我一直在争论是否要麻烦发布另一个答案:您的想法基本上是一场灾难,等待着发生。但是,我认为他们未能指出该结论背后的一些高度相关的原因

Java和C之间的差异++运行比在你似乎已经集中在细节更深。

例如,您谈论禁止在C ++中进行多重继承。首先,我要指出的是,这根本没有讲到重点。Java将“接口”定义为与“类”分开的事物。一个类仅从另一个类继承,但可以实现任意数量的接口。

C ++不会以这种方式将两个概念分开。最接近的类似物C ++提供对实现Java中的接口从另一个类继承。因此,为了使两者之间保持合理的一致性,您可能需要在C ++中使用多重继承,但是您希望以与Java相比将类与类限制接口的方式大致相同的方式来限制某些基类。它指定了功能签名,但至少在大多数情况下没有实现)。

不过,这几乎没有刮开两者之间真正差异的表面。实际上,在Java代码可能定义接口以及实现这些接口的类的情况下,C ++代码更有可能定义一些模板,这些模板将与满足其要求的任何类一起工作(而不仅仅是为实现接口而专门定义的那些模板)(s)指定)。

如果老实说,您想要的代码基本上是“使用C ++语法的Java”,那么几乎可以肯定,您必须禁止任何与后者类似的东西。您需要将模板的使用限制在Java泛型支持的大致“ T容器”级别。不幸的是,这样做将导致C ++代码混乱,以至于没有人可以维护它,而更不用说将其转换为其他编程语言的长期可能性了。

如果您确实希望将来可以将代码转换为其他某种语言,请努力使其尽可能清晰和可读。理想的情况是编写伪代码并让其执行。编写良好的现代C ++方法比您建议的子集更接近理想。无论如何编写,如果要翻译成Java,就必须实际翻译代码,而不仅仅是个别语句的语法。


许多C ++功能可以通过某些方式很好地映射到Java概念,而不能以其他方式使用。程序的某些部分将需要语言功能的重叠子集之外的内容,而尝试编写诸如“使用C ++语法的Java”之类的部分将产生可怕的混乱。另一方面,代码的许多其他部分可能需要共享子集之外的任何内容,而对于那些没有令人信服的理由需要超出共享子集的代码部分,尝试保持在共享内是有帮助的。
超级猫

大概在您说“可能需要”的地方,您的意思是:“可能不需要”?假设是这样,那么我倾向于同意。
杰里·科芬

对。如果有必要支持一个支持Java但不支持C ++的平台,则几乎可以肯定必须重新编写部分代码,而这些部分是否已使用Java友好的技术编写也无关紧要。无论如何都会被重写。但是,可以编写其他部分,而无需完全重写即可迁移它们,并且在某些情况下,这样做所需的额外成本可能很小。
超级猫

4
@supercat:OTOH,考虑到支持Java但不支持C ++的平台数量,这几乎使我成为解决问题的一种解决方案。
杰里·科芬

一段时间以来,浏览器对Java小程序的支持比对C ++小程序的支持要好。但是,浏览器对JavsScript的支持(与Java没有关系)已经得到了足够的改进,基本上已从两者同时接管。
超级猫

11

如果要使用X语言编写代码,请花一些时间正确学习该语言并使用其提供的所有功能来帮助您解决问题。当您尝试从一种语言到另一种语言的“逐字逐句”翻译时,不管是日语到英语还是Java到C ++,都会发生坏事。最好先对问题有一个很好的理解,然后以最适合所使用语言的方式来表达解决方案。

几年前,我看到了一个没有缩进的C程序,并且每个语句都从第7列开始,因为代码的作者是一个恰好在使用C的Fortran程序员。其他Fortran程序员对于这些称为指针的新事物感到不安。我不愿意提及指针,我认为它们会当场晕倒。

想象一下,雇用某人在将来维护此代码。如果这是好的C ++代码,您将可以聘请有能力的C ++开发人员,他们应该能够找到解决方法。如果它是“ C ++中的Java”,那么C ++和Java开发人员都不会很容易理解代码。


7

您所有的原因都可以被驳斥:

如果Java没有提供C ++所具有的功能,则意味着该功能不好,因此我们应避免使用它。

这并不意味着该功能不好(没有功能本来就不好)。这仅意味着该功能经常被滥用(或者由于基本概念(如直接指针与垃圾回收)而无法实现)。顾名思义,Java旨在变得更简单,对程序员更友好,因此删除了证明自己易于滥用的功能。

具有C ++特定功能(例如:朋友功能,多重继承)的C ++代码只能由C ++程序员维护或检查,但是如果我们只编写Java之类的C ++(没有C ++语言特定的功能),则两者都可以维护或检查代码。 C ++和Java程序员。

可以吗 让我们看一下最简单的C ++代码:

int len = mystring->size();

糟糕,没有使用“特定于语言的”功能,但是Java开发人员已经无法维护它!因为在Java中,您用“。”取消引用。而这里是“->。

有一天可能会要求您将代码转换为Java

或C#。或Haskell。或Python。还是露比 或COBOL(是的,您可以!)。你怎么能告诉未来?

没有C ++特定功能的代码通常更易于维护。

正好相反。引入每个功能都是为了使编程更容易,因此更易于维护。例如:选择一个在浮点数上运行的程序。现在升级它以处理复数。操作员超负荷抢救!

每个C ++语言特定的功能(例如:多重继承)都应具有在Java中实现的替代方法。如果不是这样,则意味着设计模式或代码体系结构存在问题。

但是Java具有多重继承!它称为“接口”。Java实现是一个有问题的解决方法,可以避免由于一切都源自于导致的可怕的钻石Object。通过介绍interface哪个唯一目的是不能从衍生而来Object。C ++从来没有这个问题-没有强制性的通用基础,因此每个类都可以充当没有可怕钻石的接口。

旁注:Java最近引入了...接口中的具体方法。添加了C ++始终必须解决的一个功能,该功能从未存在过。

您还只提到了很少的“ C ++具有但Java没有的东西”。C ++和Java之间最大的区别之一是对内存布局的控制。您可以创建指向对象的指针数组(就像在Java中一样),也可以创建连续的内存块。现在,如果我担心可能会误导Java开发人员的C ++功能,那么在我的列表中,像这样的隐藏和微妙的功能将比明显可见且一眼就能识别的东西(如多重继承或重载运算符)高得多。

底线:干净代码就是干净代码。Java或C ++-相同。保持简单。不必要的复杂性是错误代码的主要原因。


Java提供了某些功能和保证,而这些功能和保证在提供C ++所有功能的语言中是无法支持的。例如,一种语言在提供C ++中包含的通用形式的多重继承时,就不允许动态类型加载和Java的所有种类的身份保留类型转换。
超级猫

@supercat我不明白你为什么在这里这么说。问题不是关于不能在C ++中复制的Java功能,而是关于Java放弃的C ++功能。
Agent_L '16

我的观点是Java的某些功能未包含在C ++中,而是Java的某些功能与C ++所包含的其他功能从根本上不兼容,因此没有一种语言可以同时支持这两种语言(例如C ++ / CLI等语言)具有支持C ++功能的类型和支持Java类功能的类型,但是它们有效地存在于互斥性有限的单独Universe中)。
超级猫

6

不,您通常不应该像Java那样编写C ++,并且绝对不应该忽略Java中不存在的C ++语言功能。

一方面,Java是垃圾回收的,因此没有C ++“ delete”关键字的等效项。好的,因此您无需删除即可实现程序,因为根据您的规则,不允许这样做。

恭喜,您现在内存泄漏;)。这也不是理论上的-我已经看到这种确切的情况发生在开源游戏中。

同样,Java没有像C ++指针那样的东西,它排除了许多常见的函数调用约定。

您应该避免使用C ++的某些功能(请参见下文),但是“不使用Java”并不是一个好的测试。


更好的准则如下:

  1. 编写适合您的语言,环境和工具的代码。
  2. 编写您的团队可以理解的代码。
  3. 确保您的团队在给定领域内胜任。

项目(3)表示,如果不使用C ++进行培训,则不应拥有C ++中的Java程序员代码。有一些细微但非常重要的差异,如果他们试图将其视为Java的怪异方言,他们可能不会学到。

第(2)项表示,如果您的团队特别不喜欢多重继承(例如),并且有一个适当的解决方案不使用它,那么最好使用该替代解决方案。但是,这具体取决于您的团队。另一方面,如果您的团队对该替代解决方案比对多重继承更不满意,请使用多重继承!


最后,可以说有人应该避免使用C ++的某些语言功能。如果您想知道这些是什么,请询问C ++程序员,而不是其他语言的程序员。首先是指针算术(没有通用共识)和goto。


6

其他答案中已经有一些优点,但是我想提供一个更完整的答案,单独解决您的问题和陈述。


如果Java没有提供C ++所具有的功能,则意味着该功能不好,因此我们应避免使用它。

这已经得到了很好的回答:Java并不是C ++的“好部分”,也没有任何理由这么认为。

特别是,尽管每个单独的C ++功能的优点值得商,,但C ++ 11 / C ++ 14的许多不属于Java的功能并不一定要排除在外,因为Java设计师认为这是一个糟糕的主意。作为示例,直到版本8为止,Java都没有lambda,但是它们是在C ++ 11标准中引入到C ++的。在Java 8之前,您认为Java中缺少的C ++功能由于设计“不佳”而在设计中缺失了,这意味着作为语言功能的lambda并不“好”(到处都是LISPer的恐怖,尽管它们可能吓坏了,以至于听到您显然确实喜欢 Java)。但是现在Java设计师已经将他们的批准印章(TM)放在了lambda上,因此现在是一件好事。

更深入地讲,即使在Java 8中,lambdas-as-closures也不如C ++ 14的lambdas灵活,但这可能是由于JVM体系结构的局限性,而不是有意识地认为更灵活的方法不好。语言设计的角度。


具有C ++特定功能(例如:朋友功能,多重继承)的C ++代码只能由C ++程序员维护或检查,但是如果我们只编写Java之类的C ++(没有C ++语言特定的功能),则两者都可以维护或检查代码。 C ++和Java程序员。

这是我要回应的主要内容。

从广义上讲,从不十分熟悉您所使用的语言的程序员那里获得代码审查可能会有一些价值。他们可以为您提供有关函数/方法名称和注释的清晰度的宝贵反馈,并且(如您的问题所正确暗示的那样)(如果该语言类似于他们已经知道的一种或多种语言),他们可以遵循基本程序流程并可能捕获逻辑错误。

但是,并非这样的评论永远不会与真正了解您所使用语言的开发人员的评论“一样好”或“相当于”。从本质上讲,这是因为使一种语言看起来像另一种语言通常会隐藏细微的差异,而使一种语言表现得像另一种语言(尤其是在C ++和Java的情况下)对于该语言而言可能是惯用的,并且/或者可能仍然过于混乱给审稿人。

首先,让我们考虑一下使C ++看起来像Java意味着什么。作为一个简单的例子,您可以使用new实例化对象,就像在Java中一样:

Foo foo = new Foo();

但是以这种方式实例化的对象使用->而不是.调用方法,因此,如果希望方法调用看起来像Java,则必须编写:

Foo& foo = *new Foo();

但这是不习惯的。特别是,稍后必须使用清理内存delete &foo一些经验丰富的C ++开发人员可能甚至没有意识到这是合法代码。无论哪种方式,到处都是有趣的非Java符号,因此我们无法完全使Java语言“看起来像”。(您可以消除*new使用#define New *new,或更糟糕的是#define new *new,但是您只是乞求您的开发人员讨厌您。)而且,如上所述,deleteJava中不存在这种代码,因此无论如何(如另一个答案中所述) ),您真的无法像在Java中那样使对象使用“看起来”没有内存泄漏。

但是现代C ++包含智能共享指针,其行为与Java的内存管理变量引用非常相似。因此,您可以在Java的任何地方编写Foo foo = new Foo();,而可以编写:

std::shared_ptr<Foo> foo = std::make_shared<Foo>();

现在,您正在使用一种实际上很像Java的语言功能。但是突然之间,您有很多事情要向非C ++审阅者解释:这是shared_ptr什么?什么是微妙的棘手“陷阱” make_shared?(它使用完善转发,这会导致某些失败情况,并可能导致调用“错误的”构造函数。)为什么需要使用调用方法->,但是.编译器允许使用某些方法?(shared_ptr具有自己的方法。)如果该方法Foo::reset(void)存在,那么粗心的开发人员可能会尝试使用调用它foo.reset()(如果只有一个共享指针指向Foo该调用发生时的实例),它将删除基础内存并使null foo和Java开发人员不太可能解决此问题。

此外,C ++很多缺陷具体语言。据我所知,大多数C ++开发人员通过逐步开发自己的“安全” C ++习惯用法来学习如何应对这些陷阱,这对于他们或他们的开发团队而言往往是独一无二的(例如,参见提及该问题的现有答案)。 Google编码惯例及其评论说,“经验丰富的C ++老兵通常会拒绝Google编码准则”)。所有声称该语言可能太复杂的主张(至少以我的经验)似乎通常都带有“嗯,别用错了”的一些变体。我意识到这是对C ++社区的高度消极看法,当然,有经验的开发人员更愿意帮助语言学习者,但是确实有 似乎对未定义的行为具有一定的防御性(例如,请参见上面我的“陷阱”链接中的大部分讨论)。

Java开发人员根本无法通过代码审查来发现和纠正这些陷阱。


有一天可能会要求您将代码转换为Java。

尝试考虑在设计阶段将来您的代码可能会发生什么,这是完全有效的,甚至是值得称赞的。

但是,首先,这种特殊的考虑似乎是一种遥不可及的可能性:代码通常可以按原样重复使用(例如,您可以使用JNI接口将部分或全部有效的C ++代码插入到将来的某些Java软件中)或完全重写比直接手动“转录”。

其次,您稍后说,

每一种C ++语言特定的功能(例如:多重继承)都应具有在Java中实现的替代方法。

这实质上消除了您的“转换为Java”点。如果软件是用惯用的C ++编写的,然后转换为惯用的Java,则没有理由期望通过将C ++功能与Java功能进行精确的一对一映射来完成这种转换。


没有C ++特定功能的代码通常更易于维护。

目前尚不清楚您的意思,但实际上我对此有些同意:除非您非常谨慎,即使您非常谨慎,C ++功能也会导致可维护性问题。在C ++ FQA精简版(一个网站的语言和从别人谁至少出现真正理解它相当好它的拥护者的关键)指出,

... 80%的开发人员最多能理解20%的语言。对于不同的人来说,这是不一样的20%,所以不要指望他们理解彼此的代码。

请注意:如果您是C ++的狂热者,而您到了我的答案,并倾向于跳到评论中争论说FQA的作者实际上并不了解C ++或在大多数论点中都是虚假的,请注意,(1)在我引用他之后恰好两句话,我承认FQA是一个非常有偏见的来源,并且(2)对于我想说FQA作者是否理解C ++来说,这并不重要,而我并没有试图抨击C ++,并且您应该阅读本文章的其余部分,而不要仅仅因为我引用了FQA就认为我是反C ++的人。注释的结尾。

同样,Linus Torvalds 基本上出于这个原因讨厌C ++(警告:链接涉及很多咒骂,以真正的臭名昭著的Linus风格)。

显然,这些问题是偏颇的,但是即使是C ++的拥护者也经常说您不应该使用整个语言功能集(再次,请参阅Google编码指南;还有C ++的创建者Bjarne Stroustrup公开声明:“在C ++中,有一种更小更整洁的语言在努力摆脱困境”)。

因此,我认为C ++功能可能太容易滥用而无法使用,特别是如果您来自Java背景。此外,通过将自己局限于某种语言子集来减轻这些问题的想法是有意义的。

然而,在决定使用基于不同语言的子集,并没有看起来正确的方法,除非“不同的语言”是C,因为确实是C ++语言的类C子集。(Linus在上面的话中提到了这一点,Scott Meyers甚至将此子集称为“子语言”。)Java的运行时范式(垃圾收集,在VM上运行)与C ++根本不同,因此尚不清楚有任何有用的经验教训可以从中汲取有关C ++的用法,并且如上所述,尝试直接从Java汲取有关C ++的经验教训可能会导致非常惯用的代码。

相反,在了解如何习惯使用该语言的情况下,尝试定义该语言的“可接受子集”。如果您想要一个限制性很强的子集,而该子集仍然可以利用C所不能提供的C ++的许多功能,那么上述Google Coding指南可能是一个不错的起点。当然,您会得到开发人员说,对于Google的某些限制没有“合理的论据”,但是除非您打算雇用Alexandrescu脱离他在D语言上的工作(它本身应该告诉您一些东西),否则大概还可以。这肯定比尝试将C ++转换为Java更好。

一组代码准则的另一个良好起点是新的C ++核心准则,这是Bjarne Stroustrup和Herb Sutter正在进行的工作。

解决C ++缺点的唯一其他方法是选择其他语言。听起来您喜欢Java,并且您认为该项目最终有可能会转换为Java。如另一个答案中所述,您可以...从Java开始。

为什么您可能真的需要使用Java以外的其他东西有两个原因:

  1. 您确实需要运行时性能。在这种情况下,将C ++像Java一样对待实际上可能并没有帮助,因为像Java一样的技术(例如共享指针)会降低您的运行时性能。
  2. 您需要该软件在尚不支持JVM的晦涩平台上运行。在这种情况下,您可能会陷入具有GCC或Clang前端的语言的困扰。C和C ++是很明显的候选者,但是您也可以研究Rust。(快速插件:我还没有广泛使用Rust,但是它看起来很棒,我渴望尽快对一个主要的Rust项目进行研究,我认为考虑开始C ++项目的每个人都应该考虑使用Rust作为替代方案。)

每个C ++语言特定的功能(例如:多重继承)都应具有在Java中实现的替代方法。如果不是这样,则意味着设计模式或代码体系结构存在问题。

我已经在某种程度上解决了这个问题,但是我故意省略了您的第二句话。

我不相信像这样的东西constexpr在Java之类的部分JIT语言中没有任何意义,这表明体系结构无效。我对这样的想法持开放态度,即过度使用模板元编程可能比它值得的麻烦更多,特别是现在已经constexpr存在进行编译时函数评估的情况,但是从这种情况很明显,如果您constexpr没有设计缺陷,正在使用它:您只是确保在甚至运行代码之前就进行了一些计算,这极大地提高了性能(例如,参见Benchmark Game的n体问题的此项,其性能优于其他各项,除了另一项之外)用C ++编写


5
FQA作者肯定属于“最多了解20%的语言”组。那里有一些实际上是错误的答案,还有很多只是错了。
Ben Voigt

2
C ++ FQA中的许多(几乎全部?)都是无稽之谈。现代语言是巨大的。与Python,Ruby,Perl和Java相比,C ++很小。请教关于计算器在这些语言的一个基本问题,第一个答案是几乎不可避免线沿线的“你为什么不import SomeVeryBasicPackage和只是做这个?” 问一个先进的问题,第一个答案是几乎不可避免线沿线的“你为什么不import SomeMagicalPackage和只是做?”
David Hammen

1
@DavidHammen我认为80/20拆分是指核心语言功能,而不仅仅是标准库组件,在这种情况下,您提到的语言(可能是Perl除外)似乎并不像C ++那样大而复杂。无论如何,这只是我回答的很小一部分,我承认这是有偏见的。
凯尔·斯特兰德

1
虚拟机/托管语言在表面上显然要复杂得多,但我的意思是从使用角度来看很复杂。
凯尔·斯特兰德

1
我不知道您的理论是否正确,尽管就我而言,我肯定是分别学习C ++和C,而且我的C ++类确实很全面。但是有关20/80拆分的完整报价是,每个程序员都知道另一种 20%的语言,而大多数程序员都不能通过单独学习C语言部分来解释。无论如何,如果您想详细解释C ++如何允许更健壮的编程(我声称我以前见过但不理解),我们最好在聊天室或其他方式中进行,而不是在此处进行评论。
Kyle Strand

5

假设我只能在项目环境中使用C ++。防止使用C ++拥有但Java没有的某些语言功能是否很好(例如:多重继承,运算符覆盖)?

没有。

如果“受项目环境的限制”只能使用C ++,那么即使考虑使用任何其他技术,也没有什么要点,无论您个人希望/希望使用/推广多少。
“技术X”或“技术Y”是否支持任何给定功能,都与构建C ++应用程序的方式无关
这是一个C ++应用程序,大概出于某种“正当理由”(现在或过去),因此您应该使用特定“工具箱”提供的内容将其编写 C ++应用程序。

如果并且有将应用程序移植到其他技术的要求,那么(并且仅在那时)您可以考虑其他平台上的功能。可能发生某些事情的偶然机会是毫无意义的。但是,请记住,批量重写是一项昂贵且冒险的操作,如果没有充分的理由,管理层不太可能进行重写。

几年前,在Visual Basic [.Net]世界中曾有过类似的辩论(“使用或不使用”),有人提出了一个聪明的主意,即您应该在不使用任何[核心语言]的情况下编写Visual Basic应用程序。 Microsoft.VisualBasic命名空间提供的功能。这就像尝试编写没有std ::名称空间的C ++应用程序一样;好的,这是可能的,但是为什么在地球上任何有理智的人都愿意这样做呢?


1
关于Visual Basic的最后一点:这些供应商库的问题在于它们是专有的。使用它们意味着将自己链接到该供应商。供应商会希望您被束缚,但从长远来看,您不会:除非重写依赖于供应商特定功能的所有内容,否则您将无法使用其他供应商的软件。您使用特定于供应商的功能的次数越多,更换供应商的成本就越高,从而使您的供应商能够强制对您进行更改。这就是免费(如自由!)软件如此重要的原因之一。
cmaster

Microsoft.VisualBasic命名空间的东西是有点夸张。自从我上次使用它已经有很多年了,但是至少在开始时,它主要是为了向后兼容VB6(和/或使VB6程序员感到“自在”)。它主要以更现代(更好地集成)的形式提供了框架其余部分中已经提供的功能,因此在新项目中,很少使用它。相反,std::命名空间是整个标准库所在的位置-避免它就像避免.NET中的整个BCL。
Matteo Italia

2

当前的回答都说这是一件不好的事情,因为您应该利用所使用的语言,并解释为什么C ++功能并不仅仅因为它不在jave中就“不好”。 我将从另一个角度回答这个问题。

避免复杂的C ++功能(如多重继承,运算符重载和定义自己的模板)是一回事。

但是,除了完成最简单的任务之外,任何其他问题都无法解决:

  • 分配内存,
  • 将这些钱与积分联系起来
  • 所以建立数据结构
  • 然后允许在安全的情况下重用内存

Java和C ++没有通用的子集可以执行上述操作,因此您所要执行的操作是不可能的。(如果您问有关Java和C#的问题,您将有更多的机会,因为它们都使用垃圾收集器。)

但是,您可能需要编写代码,以便Java开发人员可以理解其功能(但不能了解详细的“方法”),这将是明智的。

您还可以设计自己的语言,以C ++和JAVA来实现。


0

没有人可以编写其他程序员不理解的任何代码。如果您认为语言锁定是个问题,请等到拥有开发人员锁定为止。一个人比另一个人更可能把你当作人质。

确实没有技术原因。您所创建的场景是出于某种原因使用某种语言的情况,但是许多当前甚至将来的开发人员并不能真正理解它。

我的猜测是,Java开发人员很可能以用Java编写的方式编写C ++,所以为什么要使其成为规则。您可能会发现一些C ++特定的功能,这些功能对于Java开发人员来说只需一些C ++指令/文档,比他们原本会陷入的大量Java混乱更容易实现和维护。

这一直是有关BASIC程序员转向面向对象语言的问题的争论。并不是说他们实施OOP做法会损害那些不了解OOP做法的新开发人员,而是他们根本不会实施。如果这样做,通常会弄错。如果某件事做得不好,则无论原因如何,都必须将其删除。

除了有人盲目地复制和粘贴代码外,他们还将在许多他们根本不了解的领域中做到这一点。

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.