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



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



Constructor::Constructor()
{
existing code

{
New code: do some new fancy stuff here
}

existing code
}


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



Edit:



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



в среда-это встроенные устройства. Существует много устаревшего кода C, завернутого в одежду C++. Есть много c оказалось C++ разработчиков.



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



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



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


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



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



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

711   14  

14 ответов:

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

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

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

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

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

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

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 в функции и в то же время, можно определить ее объем, использование дополнительных скобок. Это гарантирует, что даже если код вне дополнительных фигурных скобок может быть выполнен с помощью несколько потоки одновременно, код внутри фигурных скобок будет выполняться ровно один поток одновременно.

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

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

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

  • этот кусок имеет последовательную концептуальную цель
  • вот весь код
  • и вот комментарий о куске.

например

{  // 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, которая даже позволит вам скрыть блок.

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

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

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

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

  1. , чтобы избежать конфликта имен.
  2. в рамках using.
  3. для управления вызовом деструкторов.

пример 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 в тех случаях, когда потребность в высвобождении ресурсов естественным образом не "падает" на границы функций или структур управления.

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

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

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

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

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

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

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

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

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

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

Я согласен с"руахом". Если вы хотите хорошее объяснение различных уровней области в 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

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

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

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

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

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

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

{
   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.

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

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

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

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

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

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

Comments

    Ничего не найдено.