Dokonując dzisiaj przeglądu kodu dla kolegi, zauważyłem osobliwą rzecz. Swój nowy kod otoczył kręconymi nawiasami klamrowymi:
Constructor::Constructor()
{
existing code
{
New code: do some new fancy stuff here
}
existing code
}
Jaki jest z tego wynik? Jaki może być powód tego? Skąd pochodzi ten nawyk?
Edytować:
Na podstawie danych wejściowych i niektórych pytań poniżej czuję, że muszę dodać trochę do pytania, mimo że już zaznaczyłem odpowiedź.
Środowisko to urządzenia osadzone. Istnieje wiele starszego kodu C zawiniętego w odzież C ++. Istnieje wiele programistów C ++ w wersji C.
W tej części kodu nie ma sekcji krytycznych. Widziałem to tylko w tej części kodu. Nie są wykonywane żadne główne przydziały pamięci, tylko niektóre flagi, które są ustawione, a niektóre drżenie.
Kod otoczony nawiasami klamrowymi przypomina:
{
bool isInit;
(void)isStillInInitMode(&isInit);
if (isInit) {
return isInit;
}
}
(Nie przejmuj się kodem, po prostu trzymaj się nawiasów klamrowych ...;)) Po nawiasach klamrowych jest trochę więcej kręcenia, sprawdzania stanu i podstawowej sygnalizacji.
Rozmawiałem z facetem, a jego motywacją było ograniczenie zakresu zmiennych, konfliktów nazw i innych, których tak naprawdę nie mogłem zrozumieć.
Z mojego POV wydaje się to dość dziwne i nie sądzę, że nawiasy klamrowe powinny znajdować się w naszym kodzie. We wszystkich odpowiedziach widziałem kilka dobrych przykładów, dlaczego można otoczyć kod nawiasami klamrowymi, ale nie powinieneś zamiast tego rozdzielić kodu na metody?
źródło
Odpowiedzi:
Czasami jest to przyjemne, ponieważ daje nowy zakres, w którym możesz bardziej „czysto” zadeklarować nowe (automatyczne) zmienne.
W
C++
tym być może nie jest to tak ważne, ponieważ możesz wprowadzać nowe zmienne w dowolnym miejscu, ale być może nawyk pochodzi z tegoC
, że nie możesz tego robić aż do C99. :)Ponieważ
C++
ma destruktory, przydatne może być również automatyczne uwalnianie zasobów (plików, muteksów itp.) Po wyjściu z zakresu, co może uczynić wszystko czystszym. Oznacza to, że możesz trzymać jakiś udostępniony zasób przez krótszy czas niż w przypadku, gdybyś złapał go na początku metody.źródło
Jednym z możliwych celów jest kontrola zakresu zmiennego . A ponieważ zmienne z automatycznym zapamiętywaniem są niszczone, gdy wykraczają poza zakres, może to również umożliwić wywołanie destruktora wcześniej niż w innym przypadku.
źródło
for
wypowiedzi, aby stworzyć krótkotrwałyint i;
w C89. Z pewnością nie sugerujesz, że każdyfor
powinien mieć osobną funkcję?Dodatkowe nawiasy klamrowe służą do definiowania zakresu zmiennej zadeklarowanej w nawiasach klamrowych. Odbywa się to tak, że destruktor zostanie wywołany, gdy zmienna wyjdzie poza zasięg. W destruktorze możesz uwolnić muteks (lub dowolny inny zasób), aby inni mogli go zdobyć.
W moim kodzie produkcyjnym napisałem coś takiego:
Jak widać, w ten sposób można użyć
scoped_lock
funkcji i jednocześnie zdefiniować jej zakres za pomocą dodatkowych nawiasów klamrowych. Zapewnia to, że nawet jeśli kod poza dodatkowymi nawiasami klamrowymi może być wykonywany przez wiele wątków jednocześnie, kod wewnątrz nawiasów będzie wykonywany przez dokładnie jeden wątek na raz.źródło
scoped_lock
tego, który zostanie zniszczony na wyjątkach. Zazwyczaj wolę również wprowadzić nowy zakres blokady, ale w niektórych przypadkachunlock
jest to bardzo przydatne. Np. Aby zadeklarować nową zmienną lokalną w sekcji krytycznej, a następnie użyć jej później. (Wiem, że się spóźniłem, ale dla kompletności ...)Jak zauważyli inni, nowy blok wprowadza nowy zakres, umożliwiający napisanie odrobiny kodu z własnymi zmiennymi, które nie niszczą przestrzeni nazw otaczającego kodu i nie zużywają zasobów dłużej niż to konieczne.
Istnieje jednak inny dobry powód.
Jest to po prostu wyizolowanie bloku kodu, który osiąga określony (pod) cel. Rzadko zdarza się, że pojedyncze stwierdzenie osiąga oczekiwany przeze mnie efekt obliczeniowy; zwykle zajmuje to kilka. Umieszczenie ich w bloku (z komentarzem) pozwala mi powiedzieć czytelnikowi (często samemu w późniejszym terminie):
na przykład
Możesz argumentować, że powinienem napisać funkcję, która to wszystko zrobi. Jeśli zrobię to tylko raz, napisanie funkcji dodaje tylko dodatkową składnię i parametry; nie ma sensu. Pomyśl o tym jak o bez parametrów, anonimowej funkcji.
Jeśli masz szczęście, twój edytor będzie miał funkcję składania / rozkładania, która pozwoli nawet ukryć blok.
Robię to cały czas. Z wielką przyjemnością znam granice kodu, który muszę sprawdzić, a jeszcze lepiej wiedzieć, że jeśli ten fragment nie jest tym, czego chcę, nie muszę patrzeć na żadną z linii.
źródło
Jednym z powodów może być to, że czas życia dowolnych zmiennych zadeklarowanych w nowym bloku nawiasów klamrowych jest ograniczony do tego bloku. Innym powodem, który przychodzi mi na myśl, jest możliwość zwijania kodu w ulubionym edytorze.
źródło
Jest to to samo co blok
if
(lubwhile
etc ..), tylko bezif
. Innymi słowy, wprowadzasz zakres bez wprowadzania struktury kontrolnej.To „jawne określenie zakresu” jest zwykle przydatne w następujących przypadkach:
using
.Przykład 1:
Jeśli
my_variable
zdarza się, że jest to szczególnie dobra nazwa dla dwóch różnych zmiennych, które są używane w oderwaniu od siebie, jawne określenie zakresu pozwala uniknąć wymyślania nowej nazwy, aby uniknąć kolizji nazw.Pozwala to również uniknąć przypadkowego użycia
my_variable
poza zamierzonym zakresem.Przykład 2:
Praktyczne sytuacje, w których jest to przydatne, są rzadkie i mogą wskazywać, że kod jest gotowy do refaktoryzacji, ale mechanizm jest tam, gdzie naprawdę go potrzebujesz.
Przykład 3:
Może to być ważne dla RAII w przypadkach, gdy potrzeba uwolnienia zasobów nie „naturalnie” spada na granice funkcji lub struktur kontrolnych.
źródło
Jest to bardzo przydatne, gdy używa się zamków o zasięgu w połączeniu z krytycznymi sekcjami w programowaniu wielowątkowym. Blokada lunety zainicjowana w nawiasach klamrowych (zwykle pierwsze polecenie) wyłączy się z zakresu na końcu końca bloku, więc inne wątki będą mogły ponownie działać.
źródło
Wszyscy inni już poprawnie uwzględniali możliwości określania zakresu, RAII itp., Ale ponieważ wspominasz o środowisku osadzonym, istnieje jeszcze jeden potencjalny powód:
Być może programista nie ufa przydziałowi rejestru tego kompilatora lub chce jawnie kontrolować rozmiar ramki stosu, ograniczając jednocześnie liczbę zmiennych automatycznych w zakresie.
Tutaj
isInit
prawdopodobnie będzie na stosie:Jeśli wyjmiesz nawiasy klamrowe, miejsce
isInit
w ramce stosu może zostać zarezerwowane nawet po potencjalnym ponownym użyciu: jeśli istnieje wiele zmiennych automatycznych o podobnie zlokalizowanym zakresie, a rozmiar stosu jest ograniczony, może to stanowić problem.Podobnie, jeśli twoja zmienna jest przypisana do rejestru, wyjście poza zakres powinno dać mocną wskazówkę, że rejestr jest teraz dostępny do ponownego użycia. Będziesz musiał spojrzeć na asembler wygenerowany z nawiasami klamrowymi i bez nich, aby dowiedzieć się, czy to naprawdę robi różnicę (i profiluj go - lub obserwuj przepełnienie stosu - aby zobaczyć, czy ta różnica naprawdę ma znaczenie).
źródło
Myślę, że inni już opisywali zakres, więc wspomnę, że niepotrzebne nawiasy klamrowe mogą również służyć celowi w procesie programowania. Załóżmy na przykład, że pracujesz nad optymalizacją istniejącej funkcji. Przełączanie optymalizacji lub śledzenie błędu do określonej sekwencji instrukcji jest dla programisty proste - patrz komentarz przed nawiasami klamrowymi:
Ta praktyka jest przydatna w pewnych kontekstach, takich jak debugowanie, urządzenia osadzone lub osobisty kod.
źródło
Zgadzam się z „ruakh”. Jeśli chcesz dobrze wyjaśnić różne poziomy zakresu w C, sprawdź ten post:
Różne poziomy zakresu w aplikacji C.
Zasadniczo użycie „Blokuj zakres” jest pomocne, jeśli chcesz po prostu użyć zmiennej tymczasowej, której nie musisz śledzić przez cały czas trwania wywołania funkcji. Ponadto niektóre osoby używają go, aby dla wygody można było używać tej samej nazwy zmiennej w wielu lokalizacjach, choć ogólnie nie jest to dobry pomysł. na przykład:
W tym konkretnym przykładzie zdefiniowałem returnValue dwa razy, ale ponieważ jest on tylko w zakresie bloków, a nie w zakresie funkcji (tzn .: zakres funkcji to na przykład deklarowanie returnValue tuż po int main (void)), nie otrzymujesz błędy kompilatora, ponieważ każdy blok jest nieświadomy deklarowanej tymczasowej instancji returnValue.
Nie mogę powiedzieć, że jest to ogólnie dobry pomysł (tj .: prawdopodobnie nie powinieneś ponownie używać nazw zmiennych wielokrotnie z bloku na blok), ale ogólnie oszczędza czas i pozwala uniknąć konieczności zarządzania wartość returnValue w całej funkcji.
Na koniec zwróć uwagę na zakres zmiennych używanych w moim przykładzie kodu:
źródło
Dlaczego więc używać „niepotrzebnych” nawiasów klamrowych?
#pragma
lub zdefiniowanie „sekcji”, które można wizualizować)PS To nie jest ZŁY kod; jest w 100% poprawny. Jest to raczej kwestia (rzadkiego) smaku.
źródło
Po przejrzeniu kodu w edycji mogę powiedzieć, że niepotrzebne nawiasy klamrowe są prawdopodobnie (w widoku oryginalnych programistów) w 100% jasne, co się stanie podczas if / then, nawet jeśli teraz jest to tylko jedna linia, może to być więcej linii później, a nawiasy kwadratowe gwarantują, że nie popełnisz błędu.
jeśli powyższe było oryginalne, a usunięcie „dodatków” spowoduje, że:
późniejsza modyfikacja może wyglądać następująco:
i to oczywiście spowodowałoby problem, ponieważ teraz isInit zawsze będzie zwracany, niezależnie od tego, czy / to.
źródło
Obiekty są automatycznie niszczone, gdy wykraczają poza zakres ...
źródło
Innym przykładem użycia są klasy związane z interfejsem użytkownika, zwłaszcza Qt.
Na przykład masz skomplikowany interfejs użytkownika i wiele widżetów, każdy z nich ma własne odstępy, układ itp. Zamiast nazywać je
space1, space2, spaceBetween, layout1, ...
, możesz zapisać się przed nieopisowymi nazwami zmiennych, które istnieją tylko w dwóch-trzech wierszach kod.Cóż, niektórzy mogą powiedzieć, że powinieneś podzielić to na metody, ale tworzenie 40 metod, które nie nadają się do ponownego użycia, nie wygląda dobrze - postanowiłem po prostu dodać nawiasy klamrowe i komentarze przed nimi, więc wygląda to na logiczny blok. Przykład:
Nie można powiedzieć, że jest to najlepsza praktyka, ale jest dobra dla starszych kodów.
Wystąpiły te problemy, gdy wiele osób dodało własne komponenty do interfejsu użytkownika, a niektóre metody stały się naprawdę masywne, ale nie jest praktyczne tworzenie 40 jednorazowych metod w klasie, która już się popsuła.
źródło