Ненужные фигурные скобки в С++?

Когда я делал обзор кода для коллеги сегодня, я увидел особенную вещь. Он окружил свой новый код фигурными фигурными скобками:

Constructor::Constructor()
{
   existing code

   {
      New code: do some new fancy stuff here
   }

   existing code
}

Каков результат, если таковой имеется, из этого? Что может быть причиной для этого? Откуда эта привычка?

Edit:

На основании ввода и некоторых вопросов ниже я чувствую, что мне нужно добавить некоторые к вопросу, хотя я уже отметил ответ.

Окружающая среда - это встроенные устройства. Существует много устаревшего кода C, завернутого в одежду С++. Существует много разработчиков C, созданных на С++.

В этой части кода нет критических разделов. Я видел это только в этой части кода. Нет основных распределений памяти, только некоторые флаги, которые установлены, и некоторые бит спрятаться.

Код, окруженный фигурными фигурными скобками, выглядит примерно так:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) {
     return isInit;
   }
}

(Не обращайте внимания на код, просто придерживайтесь фигурных скобок...;)) После фигурных скобок есть еще несколько бит, проверка состояния и базовая сигнализация.

Я разговаривал с парнем, и его мотивация заключалась в том, чтобы ограничить объем переменных, называть конфликты и некоторые другие, которые я действительно не мог подобрать.

Из моего POV это кажется довольно странным, и я не думаю, что фигурные скобки должны быть в нашем коде. Я видел несколько хороших примеров во всех ответах о том, почему можно окружать код фигурными фигурными скобками, но разве вы не должны отделять код от методов?

Ответ 1

Это иногда приятно, так как он дает вам новую область, где вы можете более "чисто" объявить новые (автоматические) переменные.

В C++ это, возможно, не так важно, поскольку вы можете вводить новые переменные где угодно, но, возможно, привычка от C, где вы не могли сделать этого до C99.:)

Так как C++ имеет деструкторы, он также может быть полезен для автоматического освобождения ресурсов (файлов, мьютексов и т.д.) при выходе из области действия, что может сделать вещи более чистыми. Это означает, что вы можете удержать некоторый общий ресурс на более короткую продолжительность, чем если бы вы схватили его в начале метода.

Ответ 2

Одной из возможных целей является область управления переменной. И поскольку переменные с автоматическим хранилищем уничтожаются, когда они выходят из области видимости, это также может позволить деструктору вызываться раньше, чем в противном случае.

Ответ 3

Дополнительные привязки используются для определения области действия переменной, объявленной внутри фигурных скобок. Это делается так, что деструктор будет вызываться, когда переменная выходит за рамки. В деструкторе вы можете освободить мьютекс (или любой другой ресурс), чтобы другой мог его приобрести.

В моем производственном коде я написал что-то вроде этого:

void f()
{
   //some code - MULTIPLE threads can execute this code at the same time

   {
       scoped_lock lock(mutex); //critical section starts here

       //critical section code
       //EXACTLY ONE thread can execute this code at a time

   } //mutex is automatically released here

  //other code  - MULTIPLE threads can execute this code at the same time
}

Как вы можете видеть, таким образом вы можете использовать scoped_lock в функции и в то же время, можете определить ее область с помощью дополнительных привязок. Это гарантирует, что хотя код за пределами дополнительных фигурных скобок может выполняться потоками несколько одновременно, код внутри фигурных скобок будет выполняться ровно по одному нити за раз.

Ответ 4

Как отмечали другие, новый блок представляет новую область, позволяющую писать немного кода с его собственными переменными, которые не уничтожают пространство имен окружающего кода и не используют ресурсы больше, чем необходимо.

Однако есть еще одна прекрасная причина для этого.

Это просто изолировать блок кода, который достигает определенной (вспомогательной) цели. Редко, что один оператор достигает вычислительного эффекта, который я хочу; обычно это занимает несколько. Размещение в блоке (с комментарием) позволяет мне рассказать читателю (часто я на более поздний срок):

  • Этот кусок имеет согласованную концептуальную цель
  • Здесь нужен весь код
  • И вот комментарий о куске.

например.

{  // update the moving average
   i= (i+1) mod ARRAYSIZE;
   sum = sum - A[i];
   A[i] = new_value;
   sum = sum + new_value;
   average = sum / ARRAYSIZE ;  
}

Вы можете утверждать, что я должен написать функцию, чтобы сделать все это. Если я делаю это только один раз, запись функции просто добавляет дополнительный синтаксис и параметры; похоже немного пункт. Подумайте об этом как о беззаботной анонимной функции.

Если вам повезет, ваш редактор будет иметь функцию fold/unfold, которая даже позволит вам скрыть блок.

Я делаю это все время. Мне очень приятно знать границы кода, которые мне нужно проверить, и даже лучше знать, что если этот кусок не тот, который я хочу, мне не нужно смотреть на любую из строк.

Ответ 5

Одна из причин может заключаться в том, что время жизни любых переменных, объявленных в новом блоке фигурных скобок, ограничено этим блоком. Другая причина, которая приходит на ум, - иметь возможность использовать сворачивание кода в любимом редакторе.

Ответ 6

Это то же самое, что и блок if (или while и т.д.), без if. Другими словами, вы вводите область действия без введения структуры управления.

Это "явное определение области видимости" обычно полезно в следующих случаях:

  • Чтобы избежать конфликтов имен.
  • В область using.
  • Для управления при вызове деструкторов.

Пример 1:

{
    auto my_variable = ... ;
    // ...
}

// ...

{
    auto my_variable = ... ;
    // ...
}

Если my_variable оказывается особенно хорошим именем для двух разных переменных, которые используются изолированно друг от друга, то явное определение области позволяет вам избежать изобретения нового имени, чтобы избежать столкновения имени.

Это также позволяет избежать случайного использования my_variable из запланированного объема.

Пример 2:

namespace N1 { class A { }; }
namespace N2 { class A { }; }

void foo() {

    {
        using namespace N1;
        A a; // N1::A.
        // ...
    }

    {
        using namespace N2;
        A a; // N2::A.
        // ...
    }

}

Практические ситуации, когда это полезно, являются редкостью и могут указывать, что код созрел для рефакторинга, но механизм там, где вам когда-нибудь действительно понадобится.

Пример 3:

{
    MyRaiiClass guard1 = ...;

    // ...

    {
        MyRaiiClass guard2 = ...;
        // ...
    } // ~MyRaiiClass for guard2 called.

    // ...

} // ~MyRaiiClass for guard1 called.

Это может быть важно для RAII в тех случаях, когда необходимость освобождения ресурсов естественно не "падает" на границы функций или структур управления.

Ответ 7

Это действительно полезно при использовании заблокированных блокировок в сочетании с критическими разделами в многопоточном программировании. Ваш заблокированный замок, инициализированный в фигурных скобках (обычно первая команда), выйдет за пределы области действия в конце конца блока, и другие потоки смогут снова запустить.

Ответ 8

Все остальные уже правильно рассмотрели возможности просмотра, RAII и т.д., но поскольку вы упоминаете встроенную среду, есть еще одна потенциальная причина:

Может быть, разработчик не доверяет этому распределению регистра компилятора или хочет явно контролировать размер кадра стека, ограничивая количество автоматических переменных в области сразу.

Здесь isInit, скорее всего, будет в стеке:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) {
     return isInit;
   }
}

Если вы выберете фигурные скобки, пространство для isInit может быть зарезервировано в фрейме стека даже после того, как его можно было бы повторно использовать: если имеется множество автоматических переменных с аналогичной локализованной областью, и размер вашего стека ограничен, это может быть проблемой.

Аналогичным образом, если ваша переменная назначается регистру, выход из области действия должен обеспечивать сильную подсказку о том, что теперь доступен регистр для повторного использования. Вам нужно будет посмотреть на ассемблер, сгенерированный с помощью фигурных скобок и без них, чтобы выяснить, действительно ли это имеет значение (и профилировать его или наблюдать за переполнением стека), чтобы увидеть, действительно ли эта разница имеет значение.

Ответ 9

Я думаю, что другие уже рассмотрели область охвата, поэтому я упомянул, что ненужные фигурные скобки могут также служить целью в процессе разработки. Например, предположим, что вы работаете над оптимизацией существующей функции. Переключение оптимизации или отслеживание ошибки для определенной последовательности инструкций прост для программиста - см. Комментарий перед фигурными скобками:

// if (false) or if (0) 
{
   //experimental optimization  
}

Эта практика полезна в определенных контекстах, таких как отладка, встроенные устройства или персональный код.

Ответ 10

Я согласен с "рухах". Если вы хотите получить хорошее объяснение различных уровней охвата на C, ознакомьтесь с этим сообщением:

Различные уровни области применения в приложении C

В общем, использование "Области блока" полезно, если вы хотите просто использовать временную переменную, которую вам не нужно отслеживать в течение всего времени вызова функции. Кроме того, некоторые люди используют его, чтобы вы могли использовать одно и то же имя переменной в нескольких местах для удобства, хотя это, как правило, не очень хорошая идея. например:

int unusedInt = 1;

int main(void) {
  int k;

  for(k = 0; k<10; k++) {
    int returnValue = myFunction(k);
    printf("returnValue (int) is: %d (k=%d)",returnValue,k);
  }

  for(k = 0; k<100; k++) {
    char returnValue = myCharacterFunction(k);
    printf("returnValue (char) is: %c  (k=%d)",returnValue,k);
  }

  return 0;
}

В этом конкретном примере я дважды определил returnValue, но поскольку он находится только в области блока, а не в области функции (т.е. область действия функции будет, например, объявлять returnValue сразу после int main (void)), я не получают ошибок компилятора, так как каждый блок не обращает внимания на временный экземпляр returnValue.

Я не могу сказать, что это хорошая идея вообще (т.е. вы, вероятно, не должны повторно использовать имена переменных повторно от блока к блоку), но в целом это экономит время и позволяет избежать для управления значением returnValue по всей функции.

Наконец, обратите внимание на область значений переменных, используемых в моем примере кода:

int:  unusedInt:   File and global scope (if this were a static int, it would only be file scope)
int:  k:           Function scope
int:  returnValue: Block scope
char: returnValue: Block scope

Ответ 11

Итак, зачем использовать "лишние" фигурные скобки?

  • Для целей "Scoping" (как указано выше)
  • Сделать код более читаемым в некотором роде (в значительной степени похожим на использование #pragma или определение "разделов", которые можно визуализировать)
  • Потому что ты можешь. Просто как тот.

P.S. Это не BAD-код; он действителен на 100%. Итак, это скорее вопрос (необычный) вкус.

Ответ 12

После просмотра кода в редактировании я могу сказать, что ненужные скобки, вероятно, (в исходном представлении кодеров) будут на 100% понятны, что произойдет во время if/then, даже если это только одна строка, это может быть больше строк позже, и скобки гарантируют, что вы не сделаете ошибку.

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) {
     return isInit;
   }
   return -1;
}

если вышеуказанное было оригинальным, и удаление "дополнительных" woudl приведет к:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) 
     return isInit;
   return -1;
}

то более поздняя модификация может выглядеть так:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) 
     CallSomethingNewHere();
     return isInit;
   return -1;
}

и это, конечно, вызовет проблему, так как теперь isInit всегда будет возвращен, независимо от if/then.

Ответ 13

Объекты автоматически уничтожаются, когда они выходят из области видимости...

Ответ 14

Другим примером использования являются классы, связанные с UI, особенно Qt.

Например, у вас есть сложный интерфейс и множество виджетов, каждый из которых имеет свой собственный интервал, макет и т.д. Вместо того, чтобы называть их space1, space2, spaceBetween, layout1, ..., вы можете сэкономить себя от не описательных имен только для переменных, которые существуют в двух-трех строках кода.

Ну, некоторые могут сказать, что вы должны разделить его на методы, но создание 40 методов без повторного использования выглядит нехорошо - поэтому я решил просто добавить фигурные скобки и комментарии перед ними, поэтому он выглядит как логический блок. Пример:

// Start video button 
{ 
   <here the code goes> 
}
// Stop video button
{
   <...>
}
// Status label
{
   <...>
}

Не могу сказать, что лучшая практика, но она хорошая для устаревшего кода.

Получили эти проблемы, когда многие люди добавили свои собственные компоненты в пользовательский интерфейс, а некоторые методы стали действительно массовыми, но не практично создавать 40 встроенных в него методов использования внутри класса, которые уже перепутались.