Niepotrzebne kręcone klamry w C++?

Robiąc dzisiaj przegląd kodu dla kolegi, zobaczyłem dziwną rzecz. Otoczył swój nowy kod kręconymi klamrami w ten sposób:

Constructor::Constructor()
{
   existing code

   {
      New code: do some new fancy stuff here
   }

   existing code
}

Jaki jest wynik, jeśli w ogóle, z tego? Co może być powodem tego? Skąd się bierze ten nawyk?

Edit:

Na podstawie danych wejściowych i kilku pytań poniżej czuję, że muszę dodać kilka do pytania, mimo że już zaznaczyłem odpowiedź.

Środowisko to urządzenia wbudowane. Istnieje wiele starszego kodu C owiniętego w C++. Jest wielu programistów C++.

W tej części kodu nie ma sekcji krytycznych. Widziałem to tylko w tej części kodeksu. Nie ma większych alokacji pamięci zrobione, tylko niektóre flagi, które są ustawione, a niektóre nieco twidling.

Kod, który jest otoczony nawiasami klamrowymi, to coś w stylu:

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

(nie przejmuj się kodem, po prostu trzymaj się kręconych aparatów... ;) ) Po kręconych szelkach są trochę więcej przekręcania, sprawdzania stanu i podstawowej sygnalizacji.

Rozmawiałem z facetem i jego motywacją było ograniczenie zakresu zmiennych, starć nazewniczych i innych, których naprawdę nie mogłem złapać.

Z mojego POV to wydaje się dość dziwne i nie sądzę, że kręcone szelki powinny być w naszym kodzie. Widziałem kilka dobrych przykładów we wszystkich odpowiedziach na temat tego, dlaczego można otaczać kod nawiasami klamrowymi, ale zamiast tego nie należy rozdzielić kodu na metody?

Author: aledalgrande, 2012-03-14

14 answers

Czasami jest to miłe, ponieważ daje Ci nowy zakres, w którym możesz bardziej" czysto " deklarować nowe (automatyczne) zmienne.

W C++ to może nie jest tak ważne, ponieważ można wprowadzać nowe zmienne wszędzie, ale być może zwyczaj jest z C, gdzie nie można tego zrobić aż do C99. :)

Ponieważ C++ mA destruktory, przydatne może być również automatyczne uwalnianie zasobów (plików, muteksów, cokolwiek), gdy Zakres się kończy, co może uczynić rzeczy czystszymi. To oznacza, że ty może trzymać się jakiegoś udostępnionego zasobu przez krótszy czas niż gdybyś chwycił go na początku metody.

 255
Author: unwind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-12 07:04:22

Jednym z możliwych celów jest Kontrola zakresu zmiennej . A ponieważ zmienne z automatycznym magazynowaniem są niszczone, gdy wychodzą poza zakres, może to również umożliwić wywołanie destruktora wcześniej niż w przeciwnym wypadku.

 165
Author: ruakh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-14 14:46:01

Dodatkowe szelki są używane do definiowania zakresu zmiennej zadeklarowanej wewnątrz szelek. Robi się to tak, że Destruktor zostanie wywołany, gdy zmienna wyjdzie poza zakres. W destruktorze możesz zwolnić mutex (lub dowolny inny zasób), aby inni mogli go zdobyć.

W kodzie produkcyjnym napisałem coś takiego:

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
}

Jak widać, w ten sposób można użyć scoped_lock w funkcji i jednocześnie można zdefiniować jej zakres za pomocą dodatkowych szelek. Dzięki temu Kod poza nawiasami klamrowymi może być wykonywany przez Wiele wątków jednocześnie, kod wewnątrz nawiasów klamrowych będzie wykonywany przezdokładnie jeden wątek na raz.

 98
Author: Nawaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-14 14:54:21

Jak zauważyli inni, nowy blok wprowadza nowy zakres, umożliwiając pisanie fragmentu kodu z własnymi zmiennymi, które nie niszczą przestrzeni nazw otaczającego kodu i nie używają zasobów dłużej niż jest to konieczne.

Jest jednak inny dobry powód, by to zrobić.

Jest to po prostu wyizolowanie bloku kodu, który osiąga określony (pod) cel. Rzadko zdarza się, że pojedyncze stwierdzenie osiąga pożądany efekt obliczeniowy; zwykle zajmuje kilka. Umieszczenie tych w bloku (z komentarzem) pozwala mi powiedzieć czytelnikowi (często samemu w późniejszym terminie):

  • Ten fragment ma spójny cel pojęciowy
  • oto cały potrzebny kod
  • a oto komentarz o kawałku.

Np.

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

Można twierdzić, że powinienem napisać funkcję, aby to wszystko zrobić. Jeśli zrobię to tylko raz, pisanie funkcji po prostu dodaje dodatkową składnię i parametry; wydaje się, że nie ma sensu. Pomyśl o tym jako o parametrze bez, funkcja anonimowa.

Jeśli masz szczęście, twój edytor będzie miał funkcję składania/rozkładania, która pozwoli Ci nawet ukryć blok.

Robię to cały czas. To wielka przyjemność poznać granice kodu, które muszę sprawdzić, a jeszcze lepiej wiedzieć, że jeśli ten kawałek nie jest tym, którego chcę, nie muszę patrzeć na żadną z linii.
 45
Author: Ira Baxter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-16 21:19:43

Jednym z powodów może być to, że czas życia dowolnych zmiennych zadeklarowanych wewnątrz nowego bloku nawiasów klamrowych jest ograniczony do tego bloku. Innym powodem, który przychodzi mi na myśl, jest możliwość użycia fałdowania kodu w ulubionym edytorze.

 23
Author: arne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-14 14:41:34

Jest to to samo co if (lub while itd..) block, just without if. Innymi słowy, wprowadzasz zakres bez wprowadzania struktury kontrolnej.

[9]} Ten "jawny zakres" jest zwykle przydatny w następujących przypadkach: [12]}
  1. aby uniknąć starć nazw.
  2. do zakresu using.
  3. aby kontrolować wywołanie destruktorów.

Przykład 1:

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

// ...

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

Jeśli my_variable jest szczególnie dobra nazwa dla dwie różne zmienne, które są używane w izolacji od siebie, a następnie jawny zakres pozwala uniknąć wymyślania nowej nazwy tylko w celu uniknięcia kolizji nazwy.

Pozwala to również uniknąć przypadkowego użycia my_variable poza zamierzonym zakresem.

Przykład 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.
        // ...
    }

}

Praktyczne sytuacje, kiedy jest to przydatne, są rzadkie i mogą wskazywać, że kod jest dojrzały do refaktoryzacji, ale mechanizm jest tam, jeśli naprawdę go potrzebujesz.

Przykład 3:

{
    MyRaiiClass guard1 = ...;

    // ...

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

    // ...

} // ~MyRaiiClass for guard1 called.

Może to być ważne dla RAII w przypadkach, gdy potrzeba uwolnienia zasobów w naturalny sposób nie "spada" na granice funkcji lub struktur kontrolnych.

 16
Author: Branko Dimitrijevic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-23 22:01:58

Jest to bardzo przydatne podczas używania blokad z zakresami w połączeniu z sekcjami krytycznymi w programowaniu wielowątkowym. Twoja blokada scoped zainicjowana w nawiasach klamrowych (zwykle pierwsze polecenie) wyjdzie poza zakres na końcu bloku i inne wątki będą mogły uruchomić się ponownie.

 14
Author: learnvst,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-21 01:22:03

Wszyscy inni już poprawnie omówili scoping, RAII itp. możliwe, ale skoro wspomniałeś o środowisku wbudowanym, jest jeszcze jeden potencjalny powód:

BYĆ MOŻE programista nie ufa alokacji rejestru tego kompilatora lub chce jawnie kontrolować rozmiar ramki stosu poprzez ograniczenie liczby automatycznych zmiennych w zakresie jednocześnie.

Tutaj isInit prawdopodobnie będzie na stosie:

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

Jeśli wyjmiesz kręcone szelki, przestrzeń dla isInit może być zarezerwowane w ramce stosu nawet po tym, jak potencjalnie może być ponownie użyte: jeśli istnieje wiele automatycznych zmiennych o podobnie zlokalizowanym zakresie, a rozmiar stosu jest ograniczony, może to być problem.

Podobnie, jeśli twoja zmienna jest przypisana do rejestru, wyjście poza zakres powinno dać wyraźną wskazówkę, że rejestr jest teraz dostępny do ponownego użycia. Musisz spojrzeć na assembler wygenerowany z szelkami i bez nich, aby dowiedzieć się, czy to naprawdę robi różnicę (i profilować to - lub uważaj na przepełnienie stosu - aby sprawdzić, czy ta różnica naprawdę ma znaczenie).

 12
Author: Useless,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-15 17:03:34

Myślę, że inni już zajmowali się scopingiem, więc wspomnę, że niepotrzebne szelki mogą również służyć celowi w procesie rozwoju. Załóżmy na przykład, że pracujesz nad optymalizacją istniejącej funkcji. Przełączenie optymalizacji lub śledzenie błędu do określonej sekwencji poleceń jest proste dla programisty -- patrz komentarz przed nawiasami:

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

Ta praktyka jest przydatna w pewnych kontekstach, takich jak debugowanie, urządzenia wbudowane lub kod osobisty.

 11
Author: kertronux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-15 03:20:16

Zgadzam się z "ruakh". Jeśli chcesz dobrego wyjaśnienia różnych poziomów zakresu w C, sprawdź ten post:

Różne poziomy zakresu w aplikacji C

Ogólnie rzecz biorąc, użycie "Block scope" jest pomocne, jeśli chcesz użyć zmiennej tymczasowej, której nie musisz śledzić przez cały czas trwania wywołania funkcji. Dodatkowo, niektórzy ludzie używają go, więc można użyć tej samej nazwy zmiennej w wielu lokalizacjach dla wygody, choć nie jest to ogólnie rzecz biorąc dobry pomysł. eg:

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;
}

W tym konkretnym przykładzie zdefiniowałem returnValue dwa razy, ale ponieważ jest on tylko w zakresie bloku, zamiast zakresu funkcji(tj.: zakres funkcji byłby na przykład deklarowaniem returnValue tuż po int main (void)), nie dostaję żadnych błędów kompilatora, ponieważ każdy blok jest nieświadomy tymczasowej instancji returnValue zadeklarowanej.

Nie mogę powiedzieć, że jest to dobry pomysł w ogóle (ie: prawdopodobnie nie powinieneś wielokrotnie używać nazw zmiennych z block-to-block), ale ogólnie oszczędza czas i pozwala uniknąć konieczności zarządzania wartością returnValue w całej funkcji.

Na koniec proszę zwrócić uwagę na zakres zmiennych użytych w moim kodzie przykładowym:

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
 10
Author: DevNull,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:08

Dlaczego więc używać "niepotrzebnych" kręconych aparatów?

  • do celów "przeszukiwania" (jak wspomniano powyżej)
  • uczynienie kodu bardziej czytelnym (podobnie jak użycie #pragma lub zdefiniowanie "sekcji", które można zwizualizować)
  • Ponieważ możesz. To proste.

P. S. to nie jest zły kod, jest w 100% poprawny. Więc to raczej kwestia (niecodziennego) gustu.

 5
Author: Dr.Kameleon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-15 13:24:46

Po przejrzeniu kodu w edycji mogę powiedzieć, że niepotrzebne nawiasy są prawdopodobnie (w oryginalnym widoku koderów) w 100% jasne, co się stanie podczas if / then, nawet jeśli jest to tylko jedna linia, może być więcej linii później, a nawiasy gwarantują, że nie popełnisz błędu.

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

Jeśli powyższe było oryginalne, a usunięcie "dodatków" skutkowałoby:

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

Następnie, późniejsza modyfikacja może wyglądać tak:

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

I to, oczywiście, spowodowałoby problem, od teraz isInit będzie zawsze zwracany, niezależnie od if/then.

 5
Author: nycynik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-15 15:02:07

Obiekty są automatycznie niszczone, gdy wychodzą poza zasięg...

 4
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-14 15:45:04

Innym przykładem użycia są klasy związane z UI, zwłaszcza Qt.

Na przykład masz skomplikowany interfejs użytkownika i wiele widżetów, każdy z nich ma swój własny odstęp, układ itp. Zamiast nadawać im nazwy space1, space2, spaceBetween, layout1, ... możesz się uchronić przed nieopisowymi nazwami dla zmiennych, które istnieją tylko w dwóch-trzech liniach kodu.

Cóż, niektórzy mogą powiedzieć, że należy podzielić ją na metody, ale tworzenie 40 metod wielokrotnego użytku nie wygląda dobrze - więc postanowiłem po prostu dodać szelki i komentarze przed nimi, więc wygląda to jak logiczny blok. Przykład:

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

Nie mogę powiedzieć, że to najlepsza praktyka, ale jest dobra dla kodu źródłowego.

Pojawiły się problemy, gdy wiele osób dodało własne komponenty do interfejsu użytkownika i niektóre metody stały się naprawdę masywne, ale nie jest praktyczne tworzenie 40 metod jednorazowego użycia wewnątrz klasy, które już się zepsuły.

 1
Author: htzfun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-21 23:21:17