В C++ вам не нужно ставить фигурные скобки {} внутри регистров в операторах переключения, если только вы не объявляете переменную.
Рассмотрим следующий код:
int Lesson82a() {
int test = 1;
switch (test) {
case 1:
{
int i = 1;
cout << i << "\n";
cout << "Inside braces" << "\n";
return 0;
}
case 2:
cout << "2" << "\n";
case 3:
cout << "3" << "\n";
}
return -1;
}
int Lesson82b() {
int test = 1;
switch (test) {
case 1:
{
int i = 1;
cout << i << "\n";
}
cout << "Outside braces" << "\n";
return 0;
case 2:
cout << "2" << "\n";
case 3:
cout << "3" << "\n";
}
return -1;
}
void Lesson82() {
int test = Lesson82a();
cout << test << "\n";
test = Lesson82b();
cout << test;
}
При запуске Lesson82() результат будет следующим:
1
Inside braces
0
1
Outside braces
0
Результат тот же, но есть ли разница?
break;
после возврата, даже если тогда он недоступен?Есть ли разница при компиляции?
В строке 122 здесь: https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/begin/LearnWin32/BaseWindow/cpp/main.cpp#L122 я не мог понять почему оператор return находится за пределами фигурных скобок.
Я пробовал экспериментировать, но независимо от этого получаю одни и те же результаты в своем коде.
Один вопрос на пост.
В этом нет никакого «должен». Существуют варианты, и «лучший» вариант зависит от потребностей вашего проекта и (поскольку обычно существует несколько вариантов достижения требуемого результата) вашего мнения о том, что «лучше». А мнения как задницы – они есть у всех, и они не все одинаковы. Голосование за закрытие соответственно.
Более вопиющая проблема — провал между case 2:
и case 3:
. Решите, действительно ли вы намерены case 2:
также выполнить операторы case 3:
. Если да, добавьте атрибут [[fallthrough]];
в конце case 2:
. В противном случае добавьте break;
. Это сделано для того, чтобы читатель понял, что поведение соответствует задуманному, и когда вы правильно включаете предупреждения, компиляторы должны предупреждать об этом, если у вас их нет. И отсутствующий случай default:
обычно также вызывает сомнения в том, соответствует ли поведение во всех случаях предполагаемому.
Следуйте стандарту кодирования вашей компании или проекту кода, если таковой имеется. Если вы работаете над существующим проектом, постарайтесь обеспечить согласованность с другим кодом проекта. Если вы создаете совершенно новый проект (иногда включающий задание курса), вы можете выбрать стиль, который имеет для вас смысл.
Должен ли я всегда ставить фигурные скобки независимо от того, объявляю ли я переменную или нет?
Брекеты в этом случае не нужны, и если вам так не станет понятнее, то нет.
Если я поставлю фигурные скобки, имеет ли значение, если я помещу весь свой код в скобки? или только код, которому нужны переменные, которые я объявляю, например, в Lesson82b(), где я запускаю некоторый код и оператор возврата после фигурных скобок.
Нет, это не так. В фигурных скобках должен находиться только код, которому необходим доступ к переменной, объявленной в фигурных скобках.
Должен ли я поставить перерыв; после возвращения, даже если он недоступен?
Нет, это совершенно не нужно и, насколько я вижу, никак не улучшает чтение.
В случаях оператора switch
фигурные скобки не требуются, точка.
Назначение фигурных скобок — создать локальный лексический контекст — то есть пространство, в котором может быть создана одна или несколько локальных переменных (и, следовательно, задано определенное ограниченное время жизни). В случае предложения case
в заявлении switch
срок действия должен быть ограничен. Учитывать:
switch (quux)
{
case 1:
int n = quux / 2;
break;
case 2:
std::cout << "Hello " << n << "!\n";
break;
}
Если quux
равно 2
, то каково ожидаемое время жизни n
? Он вообще создан? Когда мы пытаемся напечатать значение n
, есть ли вообще n
для печати?
Ответы: нет. Целевая метка переключателя находится прямо над кодом, который его создает.
В C (до C23) это также синтаксическая ошибка: целью меток case
должно быть утверждение. Но int n ...
— это объявление переменной.
Sorry, I misread your language tag as C
. But I’ll leave this here since it is worthwhile info for C anyway.
Мы могли бы переместить объявление перед switch
. Давайте попробуем:
int n;
switch (quux)
{
case 1:
n = quux / 2;
break;
case 2:
std::cout << "Hello " << n << "!\n";
break;
}
Теперь, когда quux
равно 2
, мы сразу переходим к коду, который инициализирует n
значением. Когда появятся предупреждения компилятора, вы обязательно увидите что-то вроде:
error: 'n' may be used uninitialized
...это УБ .
Эти два примера — плохой код. (Ага, не делай этого.) Они существуют исключительно для того, чтобы продемонстрировать проблему.
Но предположим, что у вас есть такая конструкция:
int n;
switch (quux)
{
case 1:
n = quux / 2;
std::cout << n << "\n";
break;
case 2:
std::cout << "Hello world!\n";
break;
}
Конечно, жизнь лучше, не так ли?
Может быть. Но уже отмеченные проблемы все еще существуют. Вы не можете объявить n
локальным там, где он используется, а в тех случаях, когда он не используется, он не инициализируется. Я не могу сказать, действительно ли это имеет значение, но компилятор все равно должен об этом подумать, так что это так.
Иииии, в любом случае это хорошая идея, чтобы локальные переменные существовали только тогда и в течение настолько короткого времени, насколько это необходимо.
Решение: создайте локальный контекст, используя {
фигурные скобки }
. Так всегда было в C и, следовательно, в C++. Всегда было совершенно правильно сделать что-то вроде:
int main(void)
{
int sum = 0; // x does not exist
{ // (new local context)
int x = 10; // x exists!
while (x) sum += x--; // x exists!
} // (end of scope: x is destroyed)
printf( "%d\n", sum ); // x does not exist
return 0; // x does not exist
}
Мы можем применить эти знания для использования в утверждениях switch
.
switch (quux)
{
case 1:
{
int n = quux / 2;
std::cout << n << "\n";
}
break;
case 2:
std::cout << "Hello world!\n";
break;
}
Это совершенно нормально и справедливо. Предложения case
содержат только утверждения. n
существует только в местном контексте. Все понятно и, что немаловажно, легко рассуждать компилятору (да и нам, людям).
Теперь операторы return
и break
привязаны к очень специфическому контексту, заключенному в фигурные скобки. return
прикрепляется к контексту функции, заключенному в фигурные скобки. break
присоединяется к switch
(или контексту цикла). (Он не привязан к case
!)
Это означает, что положение return
или break
относительно локального контекста, заключенного в фигурные скобки, не имеет значения. Оба действительны:
switch (quux) | switch (quux)
{ | {
case 1: | case 1:
{ | {
int n = quux / 2; | int n = quux / 2;
std::cout << n << "\n"; | std::cout << n << "\n";
break; | }
} | break;
case 2: | case 2:
std::cout << "Hello world!\n"; | std::cout << "Hello world!\n";
break; | break;
} | }
В обоих этих случаях локальный контекст корректно завершается до того, как break
перейдет в конец всего оператора switch
.
Список операторов, следующих за меткой case
, подобен любому другому списку операторов: они могут содержать локальные контексты, заключенные в фигурные скобки, любым способом, который вы пожелаете.
switch (quux)
{
case 1:
{
std::cout << "inside\n";
}
std::cout << "outside\n";
{
std::cout << "inside again, lol\n";
}
break;
}
Просто... попробуйте написать читаемый код. Это подводит нас к последнему моменту:
Пробелы, заключенные в фигурные скобки, легко исключаются компилятором, когда они бесполезны. Поэтому использование фигурных скобок по стилистическим соображениям вполне допустимо:
switch (quux)
{
case 1:
{
int n = quux / 2;
std::cout << n << "\n";
break;
}
case 2:
{
std::cout << "Hello world!\n";
break;
}
}
Черт возьми! Мне показалось, что я прочитал C
как тег.
Этот ответ, по-видимому, охватывает то, что уже знает ОП, и не отвечает на вопрос (т. е., учитывая, что фигурные скобки необходимы для создания лексического контекста для определений переменных, следует ли использовать фигурные скобки в других ситуациях).
@JaMiT Хм, я собирался сказать, что да (даже в самом первом предложении), но, перечитывая заголовок Q, я думаю, что должен согласиться. Я отредактирую.
@JaMiT Отредактировано.
«...а что такое лучшие практики?...» обычно основано на мнениях, не относящихся к теме. Типичное руководство по стилю кодирования — следовать стилю текущего проекта.