В приведенном ниже коде у меня есть оператор while, который используется для того, чтобы убедиться, что длина входной строки меньше 10 символов. Я объявил bool
называется cont
который я использую, чтобы указать циклу while остановиться, как только мои условия будут выполнены.
#include "stdafx.h"#include <iostream>
#include <string>
int main()
{
using namespace std;
cout << "Enter a string less than 10 characters long: ";
string teststring;
{
bool cont(false);
//if input is 10 or more characters, ask for input again until it is less
while (!cont)
{
getline(cin, teststring);
if (teststring.length() >= 10)
{
cout << "Too long, try again: ";
}
else
{
cout << "Thank you.\n\n";
cont = true;
}
}
}
return 0;
}
Как вы можете видеть, я использовал набор {}
s, чтобы отделить код, давая cont
Переменная локальная область в этих скобках. Я сделал это так, чтобы, если мне когда-нибудь захотелось снова использовать это имя переменной, я мог просто повторно объявить его, и когда я закончу с ним, он будет уничтожен.
Это приемлемая практика? Или есть лучший способ сделать то, что я сделал? Я признаю, что в этом конкретном, базовом сценарии условие достаточно простое, что вряд ли необходимо, но я, возможно, захочу сделать это для более сложных циклов в будущем.
Это, конечно, игрушечный кейс, но это хорошая практика, и для этой цели существуют «автономные» блоки.
Я считаю, что это лучший способ структурировать длинные функции, чем разбивать его на элементы, которые (во многих случаях) не имеют отдельной цели.
В таком случае вы можете предоставить более быструю, ясную и безопасную программу, особенно если есть комментарий (возможно, одна строка), представляющий каждый блок.
Однако в этом случае вы можете рассмотреть:
for ( bool cont(false);!cont;)
Который имеет тот же эффект. Переменные, объявленные в for(.;.;.)
заявление ограничивается рамками этого заявления.
В этом случае вы можете увернуться от всей переменной с помощью:
for(;;) //<--- Canonical formulation for 'infinite' loop.
{
getline(cin, teststring);
if (teststring.length() >= 10)
{
cout << "Too long, try again: ";
}
else
{
cout << "Thank you.\n\n";
break; //<--- Escapes the loop.
}
}
Сноска (в ответ на комментарии):
Вы должны рассмотреть for
петля как «синтаксический сахар» на while
петля. Это не отличается в их производительности и т. Д. И просто выберите тот, который читает лучше всего. for(;cond;)
выглядит просто смешно.
Может быть крошечное (крошечное) преимущество в производительности break
но мне кажется, что во многих случаях это проще и удобочитаемее.
Если бы код был более сложным, там могло бы быть больше кода «конца цикла», поэтому он становится:
for(bool cont(false);!cont;) {
//Complex code with multiple 'exit' conditions...
if(!cont) {
//Go round again code that won't fit nicely in last term of for loop...
}
}
В то время как использование break
просто облегчает понимание «быстрого выхода».
Считается, что они не имеют «плохой кармы» goto
потому что они «идут» в очень четко определенную точку исполнения.
В общем? Да. Это хорошо.
В этом конкретном случае? Нет, ненужно. Вы не используя это имя снова, и в этом простом коде вы не собираетесь. Так что это просто шум.
Видите … это баланс.
Я обнаружил, что делаю это совсем немного в функциях, которые выполняют несколько связанных операторов SQL. Каждый раз, когда я мог бы создать его с std::stringstream
называется ss
, Конечно, я мог бы дать каждому другое имя, но это полностью предотвращает ошибки, чтобы держать каждого построителя операторов в своей области видимости.
Это также очень распространенная техника, когда вы используете такие вещи, как охранники замка.
Это приемлемая практика и делает именно то, что вы говорите. Однако он редко используется, потому что в небольших функциях это однозначно и поэтому приемлемо иметь cont
переменная в области действия верхнего уровня. Если вам кажется, что вам нужно разделить область видимости в более крупной функции, обычно предпочтительнее создать другую функцию с явным именем.
Вы можете думать об этих скобках как о безымянных функциях, вызываемых только один раз. Если вы видите, что часто используете его, возможно, вам следует дать ему собственное имя.
Другой вариант — переписать цикл, чтобы не нуждаться в cont
переменная, например:
string teststring;
do
{
cout << "Enter a string less than 10 characters long: ";
getline(cin, teststring);
} while (teststring.length() >= 10);
cout << "Thank you.\n\n";
Но это не всегда возможно, особенно если вам нужно вывести другое сообщение в зависимости от условия остановки.
Да, хорошо, если у вас есть веская причина для повторного использования переменной. Защитники блокировки — наиболее распространенное использование в моем собственном коде, и пример, приведенный в ответе Lightness о std::stringstream ss
, По сути, делайте это каждый раз, когда выбор других имен переменных кажется более неловким. Например. если ты пишешь lock1
, lock2
, lock3
… в вашем коде.
Однако более приемлемой практикой было бы рассматривать тела с длинными функциями как запах кода и преобразовывать их в свои собственные функции. Например.
...
string teststring;
{
bool cont(false);
//10 lines of code to handle the foo scenario
}
{
bool cont(false);
//15 lines of code to handle the bar scenario
}
...
Это лучше обрабатывается рефакторингом так:
...
string teststring;
foo(teststring);
bar(teststring);
...
void foo(string &teststring){
bool cont(false);
//10 lines of code
}
void bar(string &teststring){
bool cont(false);
//15 lines of code
}