C ++风格与性能?

时间:2010-11-13 09:32:54

标签: c++ c performance coding-style

C ++风格与性能 - 使用C风格的东西,比一些C ++等价物更快,这是不好的做法?例如:

  • 请勿使用atoi()itoa()atol()等!使用std::stringstream< - 有时可能会更好,但总是如此?使用C函数有什么不好的?是的,C风格,而不是C ++,但不管怎样?这是C ++,我们一直在寻找性能..

  • 永远不要使用原始指针,而是使用智能指针 - 好吧,它们真的很有用,每个人都知道,我知道,我一直都在使用它,我知道它们是多少更好的原始指针,但有时使用原始指针是完全安全的..为什么不呢? “不是C ++风格?< - 这够了吗?

  • 不要使用按位操作 - 太C风格? WTH?为什么不,当你确定你在做什么?例如 - 不要按位交换变量(a ^= b; b ^= a; a ^= b;) - 使用标准的3步交换。不要使用左移乘以2。等等。(好吧,这不是C ++风格与C风格,但仍然是“不好的做法”)

  • 最后,最昂贵的 - “不要使用enum-s返回代码,它也是C风格,使用异常来处理不同的错误”?为什么?好吧,当我们谈论深层次的错误处理时 - 好的,但为什么总是?这有什么问题,例如 - 当我们谈论一个函数时,返回不同的错误代码,当错误处理只在函数中实现时,调用第一个?我的意思是 - 不需要在上层传递错误代码。例外情况相当缓慢,对于特殊情况,它们是例外,而不是..美。

  • 等,等等。

好的,我知道良好的编码风格非常非常重要< - 代码应该易于阅读和理解。我知道微观优化没有必要,因为现代编译器非常聪明,Compiler optimizations非常强大。但我也知道exceptions handling有多昂贵,有多少(某些)smart_poin被实现,而且smart_ptr一直没有必要......我知道,例如,atoi不是那样的“安全“as std::stringstream是,但仍然......性能怎么样?


编辑:我不是在谈论一些非常难的事情,那些只是C风格的特定事物。我的意思是 - 不要错过使用函数指针或虚拟方法以及C ++程序员可能不知道的这些东西,如果从未使用过这类东西(而C程序员一直这样做)。我正在谈论一些更常见和更容易的事情,例如在例子中。

10 个答案:

答案 0 :(得分:46)

答案 1 :(得分:14)

  1. 我建议反对atoiatol,但不仅仅是基于风格。它们使得检测输入错误基本上不可能。虽然stringstream 可以执行相同的工作,但strtol(例如)是我通常建议的直接替代。< / LI>
  2. 我不确定是谁给出了这个建议。在它们有用时使用智能指针,但是当它们没有时,使用原始指针没有任何问题。
  3. 真的不知道谁认为在C ++中使用按位运算符是“不好的做法”。除非该建议附有某些特定条件,否则我认为这是完全错误的。
  4. 这个很大程度上取决于您在特殊输入和(例如)预期但不可用的输入之间绘制线的位置。一般来说,如果您直接接受来自用户的输入,则您不能(也不应该)将任何内容归类为真正特殊的内容。异常的主要优点(即使在这种情况下)确保错误不会被忽略。 OTOH,我认为这不是唯一的标准,所以你不能说它是 正确的方法来处理每一种情况。
  5. 总而言之,听起来像是你已经得到了一些教条,以至于无视现实。它可能最好被忽略或至少被视为关于如何编写C ++ 的一个相当极端的位置,而不一定是应该编写它的总是(或曾经,必然)。< / p>

答案 2 :(得分:9)

加入@Jerry Coffin的答案,我认为这个答案非常有用,我想提出一些主观意见。

  • 事情是,程序员往往会喜欢它。也就是说,我们大多数人都非常喜欢为了它而编写精美的代码。只要您自己完成项目,这就完全没问题了。请记住,一个好的软件是二进制代码按预期工作的软件,而不是源代码干净的软件。然而,当涉及由很多人开发和维护的大型项目时,编写更简单的代码在经济上更好,这样团队中的任何人都没有时间去理解你的意思。即使以运行时为代价(自然成本较低)。这就是为什么许多人,包括我自己,都会劝阻使用xor技巧而不是赋值(你可能会感到惊讶,但是有很多程序员没有听说过xor技巧)。 xor技巧无论如何都只适用于整数,并且传统的交换整数的方式非常快,所以使用xor技巧只是花哨。

  • 使用itoa,atoi等代替流更快。是的。但要快多少?不多。除非您的大多数程序只进行从文本到字符串的转换,否则您将不会注意到差异。人们为什么要使用itoa,atoi等?好吧,有些人这样做,因为他们不知道c ++替代品。另一组是因为它只是一个LOC。对于前一组 - 对你的耻辱,为后者 - 为什么不提升:: lexical_cast?

  • 例外......啊......是的,他们可以比返回代码慢,但在大多数情况下并不是真的。返回代码可以包含信息,这不是错误。应使用例外情况来报告严重错误, 不能忽略的错误 。有些人忘记了这一点,并使用异常来模拟一些奇怪的信号/插槽机制(相信我,我已经看过了,哎呀)。我的个人意见是使用返回代码没有任何问题,但是应该报告严重错误,除非分析器已经证明避免使用它们会大大提高性能

  • 原始指针 - 我自己的观点是:当它不是关于所有权时,永远不要使用智能指针。当涉及所有权时,始终使用智能指针。当然有一些例外。

  • 位移而不是乘以2的幂。我相信,这是过早优化的典型例子。 x << 3;我敢打赌,至少有25%的同事需要一些时间才能理解/意识到这意味着x * 8;混淆(至少25%)代码的具体原因是什么?再次,如果分析器告诉你这是瓶颈(我怀疑极少数情况会是这种情况),那么绿灯,继续这样做(留下评论,实际上这意味着x * 8)< / p>

总结一下。一个优秀的专业人士承认“好的风格”,理解他们为什么以及什么时候好,并且理所当然地例外,因为他知道他在做什么。平均/差的专业人员分为两类:第一类不承认好风格,甚至不了解它是什么和为什么。解雇他们。另一种类型将风格视为教条,这并不总是好的。

答案 3 :(得分:3)

什么是最佳做法? Wikipedia的话比我的好:

  

最佳做法是一种技术,   方法,过程,活动,激励,   或奖励传统智慧   认为更有效   提供特定的结果而不是   任何其他技术,方法,过程,   当应用于特定的时候   条件或情况。

     

[...]

     

仅限给定的最佳做法   适用于特定条件或   情况可能必须如此   修改或改编为类似的   情况即可。另外,一个“最好的”   练习可以演变为变得更好   随着改进的发现。

我相信在编程中没有普遍真理这样的东西:如果你认为某种东西比你所谓的“最佳实践”更适合你的情况,那就去做你认为正确的事,但要完全明白为什么你做(即用数字证明)。

答案 4 :(得分:3)

  1. 具有可变char*参数的函数在C ++中是不好的,因为手动处理它们的内存太困难了,因为我们有另外一种选择。它们不是通用的,我们无法轻松地从char切换到wchar_t,因为basic_string允许。另外lexical_cast更直接替代atoiitoa
  2. 如果你真的不需要智能指针的智能 - 请不要使用它。
  3. 要交换使用swap。仅按位操作使用按位运算 - 检查/设置/反转标志等
  4. 例外很快。它们允许删除错误检查条件分支,因此如果它们真的“永远不会发生” - 它们会提高性能。

答案 5 :(得分:2)

通过bitshifting进行乘法并不能提高C的性能,编译器会为你做到这一点。为了提高性能,请确保乘以或除以2 ^ n值。

Bitfield交换也可能会让您的编译器感到困惑。

我对C ++中的字符串处理不是很有经验,但据我所知,很难相信它比scanf和printf更灵活。

另外,这些“你永远不应该”的陈述,我通常将它们视为建议。

答案 6 :(得分:2)

你所有的问题都是先验的。我的意思是你在抽象地问他们,而不是在任何特定程序的环境中,他们的表现是你关注的。 这就像试图在没有水的情况下游泳。

如果您对特定的具体程序进行调整,您会发现性能问题,并且他们很可能几乎没有任何关于这些抽象问题的内容。他们很可能都是你以前无法想到的事情。

有关此问题的具体示例,look here

如果我可以从经验中概括,性能问题的主要来源是疾驰的普遍性。 也就是说,虽然数据结构抽象通常被认为是一件好事,但任何好东西都可能被大量过度使用,然后它就变成了一件令人沮丧的坏事。这并不罕见。根据我的经验,这是典型的。

答案 7 :(得分:1)

我认为你自己回答了问题的重要部分。我个人更喜欢易于阅读的代码(即使你理解C风格,也许下一个阅读你的代码有更多麻烦)和安全代码(建议字符串流,异常,智能指针...)

如果你确实有一些考虑按位操作的事情 - 好吧。但我经常看到C程序员使用char而不是几个bool。我不喜欢这个。

速度很重要,但程序中的一些热点通常需要大部分时间。因此,除非你测量某些技术是一个问题(或者你知道它肯定会成为一个),我宁愿使用你所谓的C ++风格。

答案 8 :(得分:1)

为什么例外的代价昂贵?例外是例外,因为它们很少见。它们的性能不会影响整体性能。为使代码异常安全而必须采取的步骤也不会影响性能。但另一方面,例外是方便和灵活的。

答案 9 :(得分:1)

这不是一个“答案”,但如果你在一个性能很重要的项目(例如嵌入式/游戏)中工作,人们通常会以你所描述的方式采用更快的C方式而不是更慢的C ++方式。 / p>

异常可能是按位操作,其中没有像您想象的那样获得。例如,“不要使用左移乘以2。”一个中等体面的编译器将为&lt;&lt;&lt;&lt;&lt;&lt; 2和* 2.