このシナリオでは、ユーザーが「0」を打った後、もう一度ガスをポンプします。これが望ましくないと仮定すると、「off-by-one エラー」と呼ばれるものがあります。次のように関数を再配置することで、これを修正 (および一時変数を削除) できます。
void GasPump::dispense()
{
while (true) {
cout << "Press any key, or enter to dispense.\n"
<< "Or press 0 to stop: \n";
if (cin.get() == '0')
break;
gasDispensed = gasDispensed + gasDispensedPerCycle;
charges = costPerGallon*gasDispensed;
displayGasNCharges();
}
}
break ステートメントの使用を避けるには、次の構造を使用できます。
bool GasPump::shouldDispenseGas()
{
cout << "Press any key, or enter to dispense.\n"
<< "Or press 0 to stop: \n";
return (cin.get() != '0');
}
void GasPump::dispense()
{
while (shouldDispenseGas()) {
gasDispensed = gasDispensed + gasDispensedPerCycle;
charges = costPerGallon*gasDispensed;
displayGasNCharges();
}
}
編集 (2011 年 9 月 27 日): @TonyK 言語が機能を提供するからといって、それを使用する必要があるわけではありません。このgoto
ステートメントは、この典型的な例です。
確かに、このような単純なループでは、関数とブレークの使用に違いはありません。どちらも明らかです。ただし、1 か月 (または数年) 後に追加の機能が追加され、ループから抜け出すための追加の条件が追加されると、非常にif
大きなループ内で複雑なロジックを含む多重ネストされたステートメントを簡単に見つけることができ、苦労します。その始まりを見つけること、ましてや出口点を見つけること。この種のコードの肥大化に対抗する方法の 1 つは、適切な名前が付けられた、短くシンプルで焦点を絞った関数を作成することです。これを行うと、コード自体が文書化されます。比較
while (true)
対
while (shouldDispenseGas())
同様に、これを STLfor_each
アルゴリズムと比較します。確かに、std::for_each(v.begin(), v.end(), &foo);
は より少し短いですfor (int i = 0; i < v.size(); ++i) { ...body of foo()... }
。しかし、本当の利点は、意図が何であるかを簡単に確認できることです。では、for_each
各要素に対して一度だけ何かを実行することがすぐにわかります。for ループでは、わかりません。ループカウンタi
は、ループ内で変更される場合があります。Abreak
も中に隠れているかもしれません。このbreak
ステートメントを回避して にロジックを埋め込むことshouldDispenseGas
で、ループが継続して終了する条件をすぐに理解できます。