准则:而vs

时间:2009-10-21 11:37:00

标签: c++ coding-style

免责声明:我试图搜索类似的问题,但这回复了每个C ++问题......我也会感激任何能提出更好标题的人。

C ++中有两个杰出的循环结构:whilefor

  • 我故意忽略do ... while构造,它是无与伦比的
  • 我知道std::for_eachBOOST_FOREACH,但不是每个循环都是for each

现在,我可能有点紧张,但它总是让我感觉正确这样的代码:

int i = 0;
while ( i < 5)
{
  // do stuff
  ++i; // I'm kind and use prefix notation... though I usually witness postfix
}

并将其转换为:

for (int i = 0; i < 5; ++i)
{
  // do stuff
}

在我看来,此示例中for的优势是多重的:

  1. 地点:变量i仅存在于循环范围内
  2. 打包:循环'控件'是打包的,所以只看循环声明我可以判断它是否正确形成(并将终止...),当然假设循环变量未在体内进一步修改
  3. 可能会内联,但我并不总是建议(这会产生棘手的错误)
  4. 我倾向于不使用while,除了while(true)成语,但这不是我曾经使用过的(双关语)。即使对于复杂的条件,我倾向于坚持for构造,尽管在多行上:

    // I am also a fan of typedefs
    for (const_iterator it = myVector.begin(), end = myVector.end();
         it != end && isValid(*it);
         ++it)
    { /* do stuff */ }
    

    当然,您可以使用while执行此操作,但之后(1)和(2)将无法验证。

    我想避免“主观”评论(“我更喜欢/更好”这种评论),我对参考现有的编码指南/编码标准非常感兴趣。

    编辑

    我倾向于尽可能地坚持(1)和(2),(1)因为推荐了地点&gt;&gt; C++ Coding Standards: Item 18,以及(2)因为如果我不必扫描整个循环来查找控制变量的可能变更(我在for时认为这是理所当然的,它会使维护更容易第3个表达式引用循环变量)。

    但是,正如gf所示,虽然有其用途:

    while (obj.advance()) {}
    

    请注意,这不是对while的咆哮,而是根据手头的情况尝试查找whilefor中的哪一个(并且出于合理的原因,不是只是喜欢)。

9 个答案:

答案 0 :(得分:7)

并非所有循环都用于迭代:

while(condition) // read e.g.: while condition holds
{
}

没关系,虽然这让人感到很不舒服:

for(;condition;)
{
}

您经常会看到任何输入源。

您可能还有隐式迭代:

while(obj.advance())
{
}

再一次,它似乎被强制用于。

此外,在强制for而不是while时,人们往往会滥用它:

for(A a(0); foo.valid(); b+=x); // a and b don't relate to loop-control

答案 1 :(得分:4)

从功能上讲,它们当然是一回事。区分的唯一原因是为维护者或代码的某些人类读者/审阅者赋予一些意义。

我认为while惯用法对于向代码的读者传达非线性测试控制循环是有用的,而习惯用法通常暗示某种序列。我的大脑也有点“期望”for循环仅由for语句参数的计数表达式部分控制,当我发现某人有条不紊地搞乱执行块内的索引变量时,我感到惊讶(和失望)。 / p>

你可以把它放在你的编码标准中,只有当遵循完整的for循环结构时才应该使用“for”循环:索引必须在初始化部分初始化,索引必须在循环测试部分中测试,并且索引的值只能在计数表达式部分中更改。任何想要更改执行块中索引的代码都应该使用while结构。基本原理是“你可以信任for循环来执行只使用你可以看到的条件,而不必寻找改变索引的隐藏语句,但你不能假设在while循环中有任何东西是真的。”

我确信有些人会争辩并找到大量的反例,以证明对于不符合我上述模型的陈述的有效用法。这很好,但考虑到你的代码对于可能没有你的洞察力或才华的维护者来说是“令人惊讶的”。最好避免意外。

答案 2 :(得分:2)

我不会在while循环中自动增加。

while (i < 5) {

   // do something with X

   if (X) {
       i++;
   }

}

答案 3 :(得分:2)

C ++中最美妙的东西之一是STL的算法部分。当使用STL读取正确编写的代码时,程序员将为reading high-level loops instead of low-level loops

答案 4 :(得分:1)

如果您选择以某种方式表达循环,我不相信编译器可以更好地优化。最后,这一切都归结为可读性,这是一个有点主观的问题(尽管大多数人可能同意大多数例子的可读性因素)。

正如您所注意到的,for循环只是一种更简洁的说法

type counter = 0;
while ( counter != end_value )
{
  // do stuff
  ++counter;
}

虽然它的语法足够灵活,允许您使用它做其他事情,但我尝试将for的使用限制为不比上面复杂得多的示例。 OTOH,我不会在上面使用while循环。

答案 5 :(得分:0)

当进行某种计数时,我倾向于使用for循环,并且当计数结束时循环结束。显然,您有标准的for( i=0; i < maxvalue; i++ ),还有for( iterator.first(); !iterator.is_done(); iterator.next() )

当不清楚循环可能迭代多少次时,我使用while循环,即“循环直到某些无法预先计算的条件成立(或无法保持)”。

答案 6 :(得分:0)

// I am also a fan of typedefs
for (const_iterator it = myVector.begin(), end = myVector.end();
     it != end && isValid(*it);
     ++it)
{ /* do stuff */ }

在我看来,上面的代码比下面的代码更不易读。

// I am also a fan of typedefs
const_iterator it = myVector.begin();
end = myVector.end();
while(it != end && isValid(*it))
{
    /* do stuff */ 
    ++it}

就个人而言,我认为可读性胜过这种格式标准。如果另一个程序员不能轻易阅读你的代码,那么最坏的情况就会导致错误,最多会导致浪费时间,这会花费公司的资金。

答案 7 :(得分:0)

在Ye Olde C中,for和while循环不一样。

不同之处在于for循环中,编译器可以自由地将变量分配给CPU寄存器并在循环后回收寄存器。因此,像这样的代码具有未定义的行为:

int i;
for (i = 0; i < N; i++) {
    if (f(i)) break;
}

printf("%d", i);           /* Non-defined behaviour, unexpected results ! */

我不是百分百肯定,但我相信这在K&R

中有所描述

这很好:

int i = 0;
while (i < N) {
    if (f(i)) break;
    i++;
}
printf("%d", i);

当然,这取决于编译器。此外,随着时间的推移,编译器不再使用这种自由,所以如果你在现代C编译器中运行第一个代码,你应该得到预期的结果。

答案 8 :(得分:0)

我不会那么快就扔掉do-while循环。如果您知道循环体至少运行一次,它们就很有用。考虑一些代码,每个CPU核心创建一个线程。使用for循环可能会出现:

for (int i = 0; i < number_of_cores; ++i)
    start_thread(i);

进入循环,首先检查的是条件,如果number_of_cores为0,在这种情况下循环体应该永远不会运行。但是,请坚持下去 - 这是一个完全多余的检查!用户将始终至少拥有一个核心,否则当前代码如何运行?编译器无法消除第一个冗余比较,据他所知,number_of_cores可能为0.但程序员知道的更好。所以,取消第一次比较:

int i = 0;
do {
    start_thread(i++);
} while (i < number_of_cores);

现在每次触发此循环时,在单核机器上只有一个比较而不是两个(带有for循环,条件对于i = 0为真,对于i = 1为假,而do为false每时每刻)。省略了第一个比较,因此速度更快。由于潜在的分支较少,分支错误预测的可能性较小,因此速度更快。因为while条件现在更可预测(在1核上总是假的),分支预测器可以做得更好,这更快。

轻微的挑剔,但不是要扔掉的东西 - 如果你知道身体总会至少运行一次,那么使用for循环是过早的悲观。