免责声明:我试图搜索类似的问题,但这回复了每个C ++问题......我也会感激任何能提出更好标题的人。
C ++中有两个杰出的循环结构:while
和for
。
do ... while
构造,它是无与伦比的std::for_each
和BOOST_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
的优势是多重的:
我倾向于不使用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
的咆哮,而是根据手头的情况尝试查找while
或for
中的哪一个(并且出于合理的原因,不是只是喜欢)。
答案 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循环是过早的悲观。