其他答案中已经有一些优点,但是我想提供一个更完整的答案,单独解决您的问题和陈述。
如果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
,但是您只是乞求您的开发人员讨厌您。)而且,如上所述,delete
Java中不存在这种代码,因此无论如何(如另一个答案中所述) ),您真的无法像在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以外的其他东西有两个原因:
- 您确实需要运行时性能。在这种情况下,将C ++像Java一样对待实际上可能并没有帮助,因为像Java一样的技术(例如共享指针)会降低您的运行时性能。
- 您需要该软件在尚不支持JVM的晦涩平台上运行。在这种情况下,您可能会陷入具有GCC或Clang前端的语言的困扰。C和C ++是很明显的候选者,但是您也可以研究Rust。(快速插件:我还没有广泛使用Rust,但是它看起来很棒,我渴望尽快对一个主要的Rust项目进行研究,我认为考虑开始C ++项目的每个人都应该考虑使用Rust作为替代方案。)
每个C ++语言特定的功能(例如:多重继承)都应具有在Java中实现的替代方法。如果不是这样,则意味着设计模式或代码体系结构存在问题。
我已经在某种程度上解决了这个问题,但是我故意省略了您的第二句话。
我不相信像这样的东西constexpr
在Java之类的部分JIT语言中没有任何意义,这表明体系结构无效。我对这样的想法持开放态度,即过度使用模板元编程可能比它值得的麻烦更多,特别是现在已经constexpr
存在进行编译时函数评估的情况,但是从这种情况很明显,如果您constexpr
没有设计缺陷,正在使用它:您只是确保在甚至运行代码之前就进行了一些计算,这极大地提高了性能(例如,参见Benchmark Game的n体问题的此项,其性能优于其他各项,除了另一项之外)用C ++编写