Po co używać przedrostków w zmiennych składowych w klasach C ++

150

W wielu kodach C ++ stosowane są konwencje składniowe do oznaczania zmiennych składowych. Typowe przykłady obejmują

  • m_ memberName dla członków publicznych (w przypadku gdy w ogóle są wykorzystywani członkowie publiczni)
  • _ nazwa_członka w przypadku członków prywatnych lub wszystkich członków

Inni próbują wymusić użycie this-> member za każdym razem, gdy używana jest zmienna składowa.

Z mojego doświadczenia wynika, że ​​większość większych baz kodu zawodzi w konsekwentnym stosowaniu takich reguł.

W innych językach konwencje te są znacznie mniej rozpowszechnione. Widzę to tylko sporadycznie w kodzie Java lub C #. Myślę, że nigdy nie widziałem tego w kodzie Ruby lub Python. Dlatego wydaje się, że w bardziej nowoczesnych językach istnieje tendencja do nieużywania specjalnych znaczników dla zmiennych składowych.

Czy ta konwencja jest nadal przydatna dzisiaj w C ++, czy jest to tylko anachronizm. Zwłaszcza, że ​​jest używany tak niespójnie w różnych bibliotekach. Czy inne języki nie wykazały, że można obejść się bez prefiksów członków?

VoidPointer
źródło
15
Preferuję to; w złożonych bazach kodów może być ważne, aby wiedzieć, które zmienne są lokalne, a które nie. Generalnie używam prefiksu zamiast wymuszania tego -> co uważam za dużo dodatkowego wpisywania i opcjonalne (podczas gdy nazewnictwo zmusi cię do tego)
Joe
5
Nigdy nie widziałeś tego w Rubim z powodu atrybutu @ for i idiomu generowania akcesorów zamiast bezpośredniego używania atrybutów.
Steve Jessop
6
Zgodnie z PEP 8 niepubliczne zmienne składowe mają być poprzedzone podkreśleniem w Pythonie (przykład:) self._something = 1.
Nathan Osman
2
Czy do ich identyfikacji nie należy używać podświetlania składni edytora?
też
2
Widzieliście już odpowiednik this->memberw kodzie Pythona. W Pythonie jest to typowe self.memberi nie jest to tylko konwencja, wymaga tego język.
matec

Odpowiedzi:

48

Musisz uważać, używając wiodącego podkreślenia. Początkowy podkreślenie przed wielką literą w słowie jest zastrzeżone. Na przykład:

_Bla

_L

to wszystkie zarezerwowane słowa

_bla

_l

nie są. Istnieją inne sytuacje, w których początkowe podkreślenia przed małymi literami są niedozwolone. W moim konkretnym przypadku okazało się, że _L jest zarezerwowane przez Visual C ++ 2005 i zderzenie spowodowało nieoczekiwane rezultaty.

Nie wiem, jak przydatne jest oznaczanie zmiennych lokalnych.

Oto link o tym, które identyfikatory są zarezerwowane: Jakie są zasady dotyczące używania podkreślenia w identyfikatorze C ++?

Juan
źródło
4
W rzeczywistości zarówno _foo, jak i _l są zarezerwowane w zakresie przestrzeni nazw.
13
Ale są w porządku jako nazwy zmiennych składowych. Nie poprzedzam podkreślenia, ponieważ zasady są zbyt zagmatwane, a ja spłonęłam w przeszłości.
Juan
11
To nie są zastrzeżone słowa. Są to nazwy zastrzeżone. Jeśli były to zastrzeżone słowa, nie można ich w ogóle użyć. Ponieważ są to nazwy zastrzeżone, możesz ich używać, ale na własne ryzyko.
TonyK
235

Jestem zwolennikiem dobrze wykonanych przedrostków .

Myślę, że (systemowa) notacja węgierska jest odpowiedzialna za większość „złego rapu”, jaki dostają przedrostki.

Ta notacja jest w dużej mierze bezcelowa w językach silnie typowanych, np. W C ++ "lpsz", aby powiedzieć, że twój łańcuch jest długim wskaźnikiem do łańcucha zakończonego znakiem nul, kiedy: segmentowana architektura jest starożytną historią, łańcuchy C ++ są według zwykłej konwencji wskaźniki zakończone znakiem nul char tablice i nie jest aż tak trudno stwierdzić, że „customerName” to ciąg znaków!

Jednak używam prefiksów, aby określić użycie zmiennej (zasadniczo „Aplikacje węgierskie”, chociaż wolę unikać terminu węgierski, ponieważ ma on złe i niesprawiedliwe skojarzenia z systemem węgierskim), co jest bardzo przydatne i pozwala zaoszczędzić czas i podejście do redukcji błędów .

Używam:

  • m dla członków
  • c dla stałych / readonlys
  • p jak wskaźnik (i pp jak wskaźnik do wskaźnika)
  • v dla lotnych
  • s dla statycznego
  • i dla indeksów i iteratorów
  • e dla wydarzeń

Tam, gdzie chcę, aby typ był jasny, używam standardowych sufiksów (np. List, ComboBox itp.).

To sprawia, że ​​programista jest świadomy użycia zmiennej za każdym razem, gdy ją widzi / używa. Prawdopodobnie najważniejszym przypadkiem jest "p" dla wskaźnika (ponieważ użycie zmienia się z var. Na var-> i musisz być dużo bardziej ostrożny ze wskaźnikami - NULL, arytmetyka wskaźników itp.), Ale wszystkie inne są bardzo przydatne.

Na przykład, możesz użyć tej samej nazwy zmiennej na wiele sposobów w jednej funkcji: (tutaj przykład w C ++, ale dotyczy to jednakowo wielu języków)

MyClass::MyClass(int numItems)
{
    mNumItems = numItems;
    for (int iItem = 0; iItem < mNumItems; iItem++)
    {
        Item *pItem = new Item();
        itemList[iItem] = pItem;
    }
}

Możesz zobaczyć tutaj:

  • Brak pomyłki między elementem a parametrem
  • Brak pomyłki między indeksem / iteratorem a elementami
  • Użycie zestawu jasno powiązanych zmiennych (lista pozycji, wskaźnik i indeks), które pozwalają uniknąć wielu pułapek związanych z nazwami rodzajowymi (niejasnymi), takimi jak „liczba”, „indeks”.
  • Prefiksy ograniczają pisanie (są krótsze i działają lepiej z automatycznym uzupełnianiem) niż alternatywy, takie jak „itemIndex” i „itemPtr”

Kolejną wielką zaletą iteratorów „iName” jest to, że nigdy nie indeksuję tablicy z niewłaściwym indeksem, a jeśli skopiuję pętlę do innej pętli, nie muszę refaktoryzować jednej ze zmiennych indeksu pętli.

Porównaj ten nierealistycznie prosty przykład:

for (int i = 0; i < 100; i++)
    for (int j = 0; j < 5; j++)
        list[i].score += other[j].score;

(co jest trudne do odczytania i często prowadzi do użycia „i” tam, gdzie „j” było zamierzone)

z:

for (int iCompany = 0; iCompany < numCompanies; iCompany++)
    for (int iUser = 0; iUser < numUsers; iUser++)
       companyList[iCompany].score += userList[iUser].score;

(który jest znacznie bardziej czytelny i eliminuje wszelkie nieporozumienia związane z indeksowaniem. Dzięki funkcji autouzupełniania we współczesnych środowiskach IDE jest to również szybkie i łatwe do wpisania)

Następną korzyścią jest to, że fragmenty kodu nie wymagają żadnego kontekstu do zrozumienia. Mogę skopiować dwa wiersze kodu do wiadomości e-mail lub dokumentu, a każdy, kto czyta ten fragment, może odróżnić wszystkie elementy członkowskie, stałe, wskaźniki, indeksy itp. Nie muszę dodawać „och, i bądź ostrożny, ponieważ „dane” to wskaźnik do wskaźnika ”, ponieważ nazywa się„ ppData ”.

Z tego samego powodu nie muszę odrywać oczu od wiersza kodu, aby go zrozumieć. Nie muszę przeszukiwać kodu, aby dowiedzieć się, czy „dane” są lokalnymi parametrami, składowymi lub stałymi. Nie muszę przesuwać ręki do myszy, więc mogę najechać kursorem na „dane”, a następnie poczekać, aż pojawi się podpowiedź (która czasami nigdy się nie pojawia). Dzięki temu programiści mogą znacznie szybciej czytać i rozumieć kod , ponieważ nie tracą czasu na wyszukiwanie w górę iw dół lub czekanie.

(Jeśli uważasz, że nie tracisz czasu na szukanie w górę iw dół, aby coś rozwiązać, znajdź kod, który napisałeś rok temu i od tamtej pory nie przeglądałeś. Otwórz plik i przeskocz mniej więcej do połowy, nie czytając go. Zobacz, jak daleko możesz przeczytać od tego punktu, zanim nie będziesz wiedział, czy coś jest elementem członkowskim, parametrem lub lokalnym. Teraz przeskocz do innej losowej lokalizacji ... To jest to, co wszyscy robimy przez cały dzień, gdy przechodzimy przez czyjś kod lub próbując zrozumieć, jak wywołać ich funkcję)

Prefiks „m” pozwala również uniknąć brzydkiej i rozwlekłej notacji „to->” (IMHO) oraz niespójności, którą gwarantuje (nawet jeśli jesteś ostrożny, zwykle kończy się to mieszanką „this-> data” i „dane” w tej samej klasie, ponieważ nic nie wymusza spójnej pisowni nazwy).

„ten” zapis ma na celu rozwiązanie niejednoznaczności - ale dlaczego ktoś miałby celowo pisać kod, który może być niejednoznaczny? Niejednoznaczność będzie prowadzić do błędów prędzej czy później. W niektórych językach „this” nie może być używane dla statycznych elementów członkowskich, więc musisz wprowadzić „specjalne przypadki” w swoim stylu kodowania. Wolę mieć jedną, prostą regułę kodowania, która ma zastosowanie wszędzie - wyraźna, jednoznaczna i spójna.

Ostatnią ważną zaletą jest Intellisense i automatyczne uzupełnianie. Spróbuj użyć Intellisense w formularzu Windows, aby znaleźć zdarzenie - musisz przewinąć setki tajemniczych metod klasy bazowej, których nigdy nie będziesz musiał wywoływać, aby znaleźć zdarzenia. Gdyby jednak każde zdarzenie miało przedrostek „e”, byłoby automatycznie wymienione w grupie pod „e”. W ten sposób prefiksowanie działa w celu grupowania członków, stałych, wydarzeń itp. Na liście Intellisense, dzięki czemu znalezienie żądanych nazw jest znacznie szybsze i łatwiejsze. (Zwykle metoda może mieć około 20-50 wartości (lokalne, parametry, składowe, stałe, zdarzenia), które są dostępne w jej zakresie. Ale po wpisaniu prefiksu (chcę teraz użyć indeksu, wpisuję „i. .. ”), mam tylko 2–5 opcji autouzupełniania.„ Dodatkowe wpisywanie ”

Jestem leniwym programistą, a powyższa konwencja oszczędza mi dużo pracy. Potrafię kodować szybciej i popełniam znacznie mniej błędów, ponieważ wiem, jak należy wykorzystać każdą zmienną.


Argumenty przeciw

Więc jakie są wady? Typowe argumenty przeciwko prefiksom to:

  • „Schematy przedrostków są złe / złe” . Zgadzam się, że "m_lpsz" i im podobne są słabo przemyślane i całkowicie bezużyteczne. Dlatego radziłbym używać dobrze zaprojektowanej notacji zaprojektowanej tak, aby spełniała twoje wymagania, zamiast kopiować coś, co jest nieodpowiednie dla twojego kontekstu. (Użyj odpowiedniego narzędzia do pracy).

  • „Jeśli zmienię użycie czegoś, muszę zmienić nazwę” . Tak, oczywiście, że tak, o to właśnie chodzi w refaktoryzacji i dlaczego IDE mają narzędzia do refaktoryzacji, które wykonują tę pracę szybko i bezboleśnie. Nawet bez prefiksów zmiana użycia zmiennej prawie na pewno oznacza, że należy zmienić jej nazwę .

  • „Prefiksy mnie mylą” . Jak każde narzędzie, dopóki nie nauczysz się go używać. Gdy twój mózg przyzwyczai się do wzorców nazewnictwa, automatycznie odfiltruje informacje i nie będziesz mieć nic przeciwko temu, że prefiksy już tam są. Ale musisz solidnie używać takiego schematu przez tydzień lub dwa, zanim naprawdę staniesz się „płynny”. I wtedy wiele osób patrzy na stary kod i zaczyna się zastanawiać, jak sobie radzili bez dobrego schematu prefiksów.

  • „Mogę po prostu spojrzeć na kod, aby rozwiązać ten problem” . Tak, ale nie musisz tracić czasu na szukanie w innym miejscu kodu lub zapamiętywanie każdego najmniejszego szczegółu, gdy odpowiedź jest natychmiastowa, na której Twoje oko jest już skupione.

  • (Niektóre z tych informacji) można znaleźć, po prostu czekając na wyświetlenie podpowiedzi w mojej zmiennej . Tak. Tam, gdzie jest to obsługiwane, dla niektórych typów prefiksów, gdy kod kompiluje się bezproblemowo, po odczekaniu możesz przeczytać opis i znaleźć informacje, które prefiks by przekazał natychmiast. Czuję, że prefiks jest prostszym, bardziej niezawodnym i wydajniejszym podejściem.

  • „To więcej pisania” . Naprawdę? Jeszcze jeden znak? A może tak jest - dzięki narzędziom do automatycznego uzupełniania IDE często ogranicza to pisanie, ponieważ każdy znak prefiksu znacznie zawęża przestrzeń wyszukiwania. Naciśnij "e", a trzy wydarzenia w twojej klasie pojawią się w trybie Intellisense. Naciśnij "c", aby wyświetlić pięć stałych.

  • „Mogę użyć this->zamiast m . Cóż, tak, możesz. Ale to po prostu dużo brzydszy i bardziej rozwlekły przedrostek! Tyle, że niesie ze sobą dużo większe ryzyko (szczególnie w zespołach), ponieważ dla kompilatora jest opcjonalny , a zatem często jego użycie jest niespójne. mz drugiej strony jest zwięzła, jasna, jednoznaczna i nie jest opcjonalna, więc przy jej użyciu o wiele trudniej jest popełnić błąd.

Jason Williams
źródło
6
Chodzi mi o to, że czytałem, że problem z notacją węgierską wynika właśnie z niezrozumienia Simonyi. Napisał przedrostek, który powinien być używany do wskazania typu zmiennej, w której miał na myśli „typ”, jak w „rodzaj rzeczy”, a nie dosłowny typ danych. Później chłopaki z platformy Microsoft podnieśli go i wymyślili lpsz ... a reszta jest historią ...
VoidPointer
19
„s jest dla statycznego” brzmi dla mnie jak „zła” forma węgierskiego.
jalf,
6
@Mehrdad: Myślę, że nie zjest to bardzo przydatne w języku takim jak C ++, w którym tego rodzaju szczegóły implementacji niskiego poziomu powinny być zawarte w klasie, ale w C (gdzie zerowe zakończenie jest ważną różnicą) Zgadzam się z tobą. IMO każdy schemat, którego używamy, powinien być dostosowany zgodnie z wymaganiami, aby najlepiej odpowiadał naszym własnym potrzebom - Więc jeśli zerowe zakończenie wpływa na użycie zmiennej, nie ma nic złego w zadeklarowaniu „z” użytecznego przedrostka.
Jason Williams
14
The most important case is "p" for pointer (because the usage changes from var. to var-> and you have to be much more careful with pointers.Z całego serca się nie zgadzam. Jeśli niewłaściwie użyję wskaźnika, po prostu się nie skompiluje ( void*może to być jednak wyjątek dla podwójnych wskaźników). I cały ->nad .wystarczy mi powiedzieć, że jest to wskazówka. Ponadto, jeśli używasz autouzupełniania, twój edytor prawdopodobnie ma podpowiedzi deklaracji, eliminując potrzebę przedrostków dla informacji zmiennych. Niezależnie od tego, dobra odpowiedź.
Thomas Eding
5
Głosowano za jasnymi, wyczerpującymi i interesującymi wyjaśnieniami, jednak niewiele tutaj sugeruje, jak oszczędza to czas w C ++, JESZCZE pozostaje w dużej mierze niewykorzystane w wielu innych językach.
115

Generalnie nie używam przedrostka dla zmiennych składowych.

Kiedyś używałem mprefiksu, dopóki ktoś nie wskazał, że „C ++ ma już standardowy prefiks dostępu do członków:this-> .

Więc tego teraz używam. Oznacza to, że w przypadku niejednoznaczności dodaję rozszerzeniethis-> przedrostek, ale zwykle nie ma dwuznaczności i mogę po prostu odwołać się bezpośrednio do nazwy zmiennej.

Dla mnie to najlepsze z obu światów. Mam prefiks, którego mogę użyć, gdy go potrzebuję, i mogę go pominąć, gdy tylko jest to możliwe.

Oczywiście oczywistym przeciwieństwem tego jest „tak, ale wtedy nie można od razu zobaczyć, czy zmienna jest członkiem klasy, czy nie”.

Na co mówię „i co z tego? Jeśli chcesz to wiedzieć, Twoja klasa prawdopodobnie ma zbyt duży stan. Lub funkcja jest zbyt duża i skomplikowana”.

W praktyce stwierdziłem, że działa to wyjątkowo dobrze. Jako dodatkowy bonus pozwala mi łatwo promować zmienną lokalną dla członka klasy (lub odwrotnie), bez konieczności zmiany jej nazwy.

A co najważniejsze, jest spójny! Nie muszę robić nic specjalnego ani pamiętać o żadnych konwencjach, aby zachować spójność.


Nawiasem mówiąc, nie powinieneś używać wiodących podkreśleń dla członków swojej klasy. Nieprzyjemnie zbliżasz się do nazw zarezerwowanych przez implementację.

Standard zastrzega sobie wszystkie nazwy zaczynające się od podwójnego podkreślenia lub podkreślenia, po którym następuje duża litera. Zastrzega również wszystkie nazwy zaczynające się od pojedynczego podkreślenia w globalnej przestrzeni nazw .

Tak więc członek klasy z początkowym podkreśleniem, po którym następuje mała litera, jest legalny, ale wcześniej czy później zrobisz to samo z identyfikatorem zaczynającym się od dużej litery lub w inny sposób złamiesz jedną z powyższych zasad.

Dlatego łatwiej jest po prostu uniknąć wiodących podkreśleń. Użyj podkreślenia postfiksowego, lub po m_prostu mprzedrostka, jeśli chcesz zakodować zakres w nazwie zmiennej.

jalf
źródło
„Tak więc członek klasy z początkowym podkreśleniem, po którym następuje mała litera, jest legalny, ale wcześniej czy później zrobisz to samo z identyfikatorem zaczynającym się od dużej litery lub w inny sposób złamiesz jedną z powyższych zasad”. - zmienne składowe klasy nie znajdują się w globalnej przestrzeni nazw, więc początkowy znak podkreślenia jest bezpieczny, niezależnie od tego, czy następuje po nim mała, czy duża litera.
mbarnett
3
@mbarnett: Nie, podkreślenie zakończone wielką literą jest ogólnie zarezerwowane , nie tylko w globalnej przestrzeni nazw.
jalf,
9
zaskoczony, że głos tej odpowiedzi jest mniejszy niż przedrostek.
Marson Mao
Zgadzam się z tą odpowiedzią, po prostu użyj, this->jeśli chcesz określić, że jest to zmienna składowa, lub nie, to też jest dobre.
David Morton,
Co więcej, nie musisz dokumentować swojej konwencji, aby przekazać swój kod innym osobom. Wszyscy rozumieją, co this->to znaczy.
Caduchon
34

Wolę podkreślenia postfix, takie jak:

class Foo
{
   private:
      int bar_;

   public:
      int bar() { return bar_; }
};
jkeys
źródło
Ja też. Daję również akcesorom / mutatorom tę samą nazwę.
Rob
4
Ciekawy. Na początku wygląda trochę brzydko, ale widzę, jak może to być korzystne.
ya23
6
Powiedziałbym, że jest dużo mniej brzydki niż: „mBar” lub „m_bar”.
sydan
6
ale masz vector<int> v_;i pisanie też v_.push_back(5)jest dość brzydkie
avim
4
To styl Google C ++.
Justme0
20

Ostatnio wolałem prefiks m_ zamiast w ogóle nie mieć przedrostka, powody nie są tak duże, że ważne jest oznaczanie zmiennych składowych, ale to, że unika się niejednoznaczności, powiedzmy, że masz kod taki jak:

void set_foo(int foo) { foo = foo; }

Przyczyna nie działa, foodozwolona jest tylko jedna . Masz więc opcje:

  • this->foo = foo;

    Nie podoba mi się to, ponieważ powoduje cieniowanie parametrów, nie można już używać g++ -Wshadowostrzeżeń, wtedy też dłużej można pisać m_. Nadal napotykasz konflikty nazewnictwa między zmiennymi i funkcjami, gdy masz a int foo;i a int foo();.

  • foo = foo_; lub foo = arg_foo;

    Używam tego od jakiegoś czasu, ale sprawia to, że listy argumentów są brzydkie, dokumentacja nie powinna zajmować się niejednoznacznością nazw w implementacji. Istnieją również konflikty nazewnictwa między zmiennymi i funkcjami.

  • m_foo = foo;

    Dokumentacja API pozostaje czysta, nie ma niejednoznaczności między funkcjami składowymi i zmiennymi, a wtedy jest krótszy do wpisania this-> . Jedyną wadą jest to, że sprawia, że ​​struktury POD są brzydkie, ale ponieważ struktury POD nie cierpią z powodu niejednoznaczności nazwy, nie trzeba jej z nimi używać. Posiadanie unikalnego prefiksu ułatwia również kilka operacji wyszukiwania i zamiany.

  • foo_ = foo;

    Większość zalet m_zastosowania, ale odrzucam to ze względów estetycznych, końcowe lub wiodące podkreślenie sprawia, że ​​zmienna wygląda na niekompletną i niezrównoważoną. m_po prostu wygląda lepiej. Za pomocąm_ jest również bardziej rozszerzalne, ponieważ można go używać g_do globalnych i s_statycznych.

PS: Powodem, dla którego nie widzisz tego m_w Pythonie lub Ruby, jest to, że oba języki wymuszają własny prefiks, Ruby używa @dla zmiennych składowych, a Python wymaga self..

Grumbel
źródło
1
aby być uczciwym, przegapiłeś co najmniej 2 inne opcje, np. (a) użyj pełnych nazw, jak footylko dla członków, a zamiast tego użyj jednoliterowych lub krótkich nazw parametrów lub innych miejscowych / rzeczy odrzuconych, takich jak int f; lub (b) poprzedzić coś parametrami lub innymi lokalnymi wartościami. dobra uwaga w kwestii m_i strąków; Niezależnie doszedłem do wniosku, że w większości przypadków wolę przestrzegać obu tych wytycznych.
underscore_d
12

Podczas czytania funkcji składowej wiedza o tym, kto jest „właścicielem” każdej zmiennej, jest absolutnie niezbędna do zrozumienia znaczenia zmiennej. W takiej funkcji:

void Foo::bar( int apples )
{
    int bananas = apples + grapes;
    melons = grapes * bananas;
    spuds += melons;
}

... łatwo jest zobaczyć, skąd pochodzą jabłka i banany, ale co z winogronami, melonami i kiełkami? Czy powinniśmy szukać w globalnej przestrzeni nazw? W deklaracji klasy? Czy zmienna należy do tego obiektu, czy do klasy tego obiektu? Bez znajomości odpowiedzi na te pytania nie możesz zrozumieć kodu. W dłuższej funkcji nawet deklaracje zmiennych lokalnych, takich jak jabłka i banany, mogą się zgubić w tasowaniu.

Dołączenie spójnej etykiety dla zmiennych globalnych, zmiennych składowych i statycznych zmiennych składowych (być może odpowiednio g_, m_ i s_) natychmiast wyjaśnia sytuację.

void Foo::bar( int apples )
{
    int bananas = apples + g_grapes;
    m_melons = g_grapes * bananas;
    s_spuds += m_melons;
}

Na początku może się do tego przyzwyczaić - ale co w programowaniu nie? Był dzień, kiedy nawet {i} wyglądali dla ciebie dziwnie. A kiedy już się do nich przyzwyczaisz, pomogą ci znacznie szybciej zrozumieć kod.

(Użycie „this->” zamiast m_ ma sens, ale jest jeszcze bardziej rozwlekłe i wizualnie destrukcyjne. Nie uważam tego za dobrą alternatywę do oznaczania wszystkich zastosowań zmiennych składowych).

Możliwym sprzeciwem wobec powyższego argumentu byłoby rozszerzenie argumentu na typy. Może być również prawdą, że znajomość typu zmiennej „jest absolutnie niezbędna do zrozumienia znaczenia zmiennej”. Jeśli tak jest, dlaczego nie dodać przedrostka do każdej nazwy zmiennej, która identyfikuje jej typ? Kierując się tą logiką, otrzymujesz notację węgierską. Ale wielu ludzi uważa, że ​​notacja węgierska jest pracochłonna, brzydka i nieprzydatna.

void Foo::bar( int iApples )
{
    int iBananas = iApples + g_fGrapes;
    m_fMelons = g_fGrapes * iBananas;
    s_dSpuds += m_fMelons;
}

język węgierski takpowiedz nam coś nowego o kodzie. Teraz rozumiemy, że w funkcji Foo :: bar () istnieje kilka niejawnych rzutów. Problem z kodem polega teraz na tym, że wartość informacji dodanych przez węgierskie prefiksy jest niewielka w stosunku do kosztu wizualnego. System typów C ++ zawiera wiele funkcji, które pomagają typom dobrze współpracować lub zgłaszać ostrzeżenie lub błąd kompilatora. Kompilator pomaga nam radzić sobie z typami - nie potrzebujemy do tego notacji. Możemy łatwo wywnioskować, że zmienne w Foo :: bar () są prawdopodobnie numeryczne, a jeśli to wszystko, co wiemy, to wystarczy, aby uzyskać ogólne zrozumienie funkcji. Dlatego wartość znajomości dokładnego typu każdej zmiennej jest stosunkowo niewielka. Jednak brzydota zmiennej takiej jak „s_dSpuds” (lub nawet po prostu „dSpuds”) jest świetna. Więc,

OldPeculier
źródło
Dzięki za pomysł s_. Wydaje się bardzo przydatne i jakoś nigdy nie przyszło mi do głowy.
Chris Olsen
10

Nie mogę powiedzieć, jak bardzo jest to rozpowszechnione, ale osobiście zawsze (i zawsze) poprzedzałem zmienne składowe „m”. Na przykład:

class Person {
   .... 
   private:
       std::string mName;
};

To jedyna forma przedrostka, której używam (jestem bardzo anty węgierską notacją), ale przez lata zapewniała mi dobrą pozycję. Tak na marginesie, generalnie nie znoszę używania podkreśleń w nazwach (lub gdziekolwiek indziej), ale robię wyjątek dla nazw makr preprocesora, ponieważ zwykle wszystkie są pisane wielkimi literami.


źródło
5
Problem z używaniem m zamiast m_ (lub _) polega na tym, że obecna moda na wielbłądy sprawia, że ​​czytanie niektórych nazw zmiennych jest trudne.
Martin Beckett
1
@Neil Jestem z tobą. @mgb: Nienawidzę nazw zaczynających się od „_”. To tylko zaproszenie do tego, żeby coś poszło nie tak.
Martin York
1
@Neil: Jakiej konwencji używasz wtedy, jeśli nie używasz podkreśleń i nie używasz camelcase?
jalf
2
Zrozumiałem, że to camelCase sprawia, że ​​używanie tylko m dla zmiennych, takich jak „apData”, jest mylące - staje się „mapData” zamiast „m_apData”. Używam _camelCase dla chronionych / prywatnych zmiennych składowych, ponieważ się wyróżnia
Martin Beckett
10
@MartinBeckett: Powinieneś wykorzystać aw tym scenariuszu - inaczej nie robisz tego dobrze. mApData( mprzedrostek, a następnie nazwa zmiennej to apData).
Platinum Azure
8

Głównym powodem przedrostka składowego jest rozróżnienie między lokalną funkcją składową a zmienną składową o tej samej nazwie. Jest to przydatne, jeśli używasz getterów z nazwą rzeczy.

Rozważać:

class person
{
public:
    person(const std::string& full_name)
        : full_name_(full_name)
    {}

    const std::string& full_name() const { return full_name_; }
private:
    std::string full_name_;
};

W tym przypadku nie można nazwać zmiennej składowej full_name. Musisz zmienić nazwę funkcji składowej na get_full_name () lub jakoś udekorować zmienną składową.

Wilk
źródło
1
To jest powód, dla którego przedrostek. Myślę, że foo.name()jest dużo bardziej czytelny niż foo.get_name()moim zdaniem.
Terrabits,
6

Nie sądzę, aby jedna składnia miała prawdziwą wartość nad inną. Wszystko sprowadza się, jak wspomniałeś, do jednolitości wszystkich plików źródłowych.

Jedynym punktem, w którym uważam takie zasady za interesujące, jest sytuacja, gdy potrzebuję 2 rzeczy o identycznych nazwach, na przykład:

void myFunc(int index){
  this->index = index;
}

void myFunc(int index){
  m_index = index;
}

Używam go do rozróżnienia tych dwóch. Również kiedy zawijam wywołania, jak z Windows Dll, RecvPacket (...) z Dll może być zawijane w RecvPacket (...) w moim kodzie. W takich szczególnych przypadkach użycie przedrostka takiego jak „_” może sprawić, że te dwa elementy będą podobne, łatwe do zidentyfikowania, który jest który, ale inny dla kompilatora

Eric
źródło
6

Niektóre odpowiedzi koncentrują się na refaktoryzacji, a nie konwencjach nazewnictwa, jako sposobie poprawy czytelności. Nie wydaje mi się, że jedno może zastąpić drugie.

Znam programistów, którzy nie lubią używać lokalnych deklaracji; wolą umieszczać wszystkie deklaracje na górze bloku (jak w C), aby wiedzieli, gdzie je znaleźć. Zauważyłem, że tam, gdzie pozwala na to określanie zakresu, deklarowanie zmiennych tam, gdzie są one używane po raz pierwszy, skraca czas, który spędzam na spoglądaniu wstecz, aby znaleźć deklaracje. (Dotyczy to nawet małych funkcji). Ułatwia mi to zrozumienie kodu, na który patrzę.

Mam nadzieję, że jest wystarczająco jasne, jak to się ma do konwencji nazewnictwa członków: Kiedy członkowie są jednolicie poprzedzeni, nigdy nie muszę w ogóle patrzeć wstecz; Wiem, że deklaracja nie zostanie nawet znaleziona w pliku źródłowym.

Jestem pewien, że nie zaczynałam preferować te style. Jednak z biegiem czasu, pracując w środowiskach, w których były one konsekwentnie używane, zoptymalizowałem swoje myślenie, aby je wykorzystać. Myślę, że jest możliwe, że wielu ludzi, którzy obecnie czują się z nimi niekomfortowo, również by je preferowali, biorąc pod uwagę konsekwentne stosowanie.

Dan Breslau
źródło
5

Te konwencje są po prostu tym. Większość sklepów stosuje konwencje kodu, aby ułatwić czytelność kodu, dzięki czemu każdy może łatwo spojrzeć na fragment kodu i szybko rozszyfrować między takimi elementami, jak członkowie publiczni i prywatni.

Panie Will
źródło
„między członkami publicznymi i prywatnymi” - jak często to się dzieje? Nie przypominam sobie, żebym to widział, ale z drugiej strony nie przeglądam baz kodu ani nic takiego.
underscore_d
Nie robię tego we własnym kodowaniu, ale pracowałem w miejscach, w których musieliśmy to robić w oparciu o ich przewodniki po konwencji kodowania. Wolę tego nie robić, ponieważ prawie wszystkie IDE będą wyświetlać zmienne prywatne w innym kolorze.
Pan Will
Hmm, wydaje mi się, że zdarza się to tylko w innych sytuacjach niż moja. Zwykle używam albo classes, których wszyscy członkowie są private/ protected, albo POD structs, których wszystkie zmienne są public(i często również const). Dlatego nigdy nie muszę się zastanawiać nad poziomem dostępu danego członka.
underscore_d
5

Inni próbują wymusić użycie this-> member za każdym razem, gdy używana jest zmienna składowa

Dzieje się tak zwykle dlatego, że nie ma przedrostka . Kompilator potrzebuje wystarczającej ilości informacji, aby rozwiązać daną zmienną, czy to unikatową nazwę ze względu na prefiks, czy za pośrednictwemthis słowa kluczowego.

Więc tak, myślę, że przedrostki są nadal przydatne. Ja na przykład wolałbym wpisać „_”, aby uzyskać dostęp do członka, zamiast „this->”.

Kent Boogaart
źródło
3
kompilator i tak może rozwiązać ten problem ... zmienne lokalne będą ukrywać te o wyższym zakresie w większości języków. Ma to na celu (wątpliwą) korzyść dla ludzi czytających kod. Każde przyzwoite środowisko IDE będzie wyróżniać lokalnych / członków / globalnych na różne sposoby, więc nie ma potrzeby tego rodzaju rzeczy
rmeador
1
Dokładnie. Miejscowi będą ukrywać członków klasy. Rozważmy konstruktora, który ustawia te elementy członkowskie. Zwykle ma sens nazywanie parametrów tak samo, jak członkom.
Kent Boogaart
6
Dlaczego to zapach kodu? Powiedziałbym, że to całkiem pospolite i rozsądne, zwłaszcza jeśli chodzi o konstruktorów.
Kent Boogaart
3
Konstruktor powinien (ogólnie) ustawić lokalne na swojej liście inicjalizacyjnej. I tam parametry nie są ukrytymi nazwami pól, ale oba są dostępne - więc możesz pisaćstruct Foo { int x; Foo(int x) : x(x) { ... } };
Pavel Minaev
2
Zakładam, że problem pojawia się, gdy to zrobisz Foo(int x, bool blee) : x(x) { if (blee) x += bleecount; } // oops, forgot this->Wolę nazywać moje zmienne składowe czymś użytecznym, a następnie podawać parametry konstruktora, które do nich pasują, skrócone nazwy:Foo(int f) : foo(f) {...}
Steve Jessop
4

Inne języki będą używać konwencji kodowania, po prostu są inne. Na przykład C # ma prawdopodobnie dwa różne style, których ludzie zwykle używają, albo jedną z metod C ++ (_variable, mVariable lub inny prefiks, taki jak notacja węgierska), lub to, co nazywam metodą StyleCop.

private int privateMember;
public int PublicMember;

public int Function(int parameter)
{
  // StyleCop enforces using this. for class members.
  this.privateMember = parameter;
}

W końcu staje się tym, co ludzie wiedzą i co wygląda najlepiej. Osobiście uważam, że kod jest bardziej czytelny bez notacji węgierskiej, ale znalezienie zmiennej z inteligencją może być łatwiejsze, na przykład, jeśli dołączona jest notacja węgierska.

W powyższym przykładzie nie potrzebujesz prefiksu m dla zmiennych składowych, ponieważ poprzedzasz to użycie. wskazuje to samo w metodzie wymuszonej przez kompilator.

Niekoniecznie oznacza to, że inne metody są złe, ludzie trzymają się tego, co działa.

Will Eddins
źródło
3

Kiedy masz dużą metodę lub bloki kodu, wygodnie jest od razu wiedzieć, czy używasz zmiennej lokalnej, czy elementu członkowskiego. ma to na celu uniknięcie błędów i lepszą przejrzystość!

Matthieu
źródło
3
Jeśli masz dużą metodę, dla lepszej przejrzystości przerwij ją.
sbi
4
Istnieje wiele powodów, aby nie łamać niektórych dużych metod. Na przykład, jeśli twoja metoda musi zachować wiele stanu lokalnego, musisz albo przekazać wiele parametrów do metod podrzędnych, utworzyć nowe klasy, które istnieją wyłącznie w celu przekazywania danych między tymi metodami lub zadeklarować dane stanu jako dane składowe klasy nadrzędnej. Wszystkie z nich mają problemy, które mogłyby wpłynąć na przejrzystość lub łatwość utrzymania metody w porównaniu z pojedynczą długą metodą (zwłaszcza taką, której logika jest prosta).
Steve Broberg
3
@sbi: Wytyczne są po prostu takie; wytyczne, a nie zasady. Czasami potrzebujesz dużych metod, które logicznie nie dają się rozdzielić, a czasami nazwy parametrów kolidują ze składnikami.
Ed S.
Prosimy nie upubliczniać zmiennych składowych. Po prostu użyj akcesoriów. Nawiasy powinny informować czytelnika, że ​​jest to zmienna składowa.
jkeys
Zwróć uwagę, że w gcc (> = 4.6) jest ostrzeżenie dotyczące wykrywania -Wshadow
kolizji
3

IMO, to sprawa osobista. W ogóle nie umieszczam żadnych przedrostków. W każdym razie, jeśli kod ma być publiczny, myślę, że powinien mieć kilka przedrostków, więc może być bardziej czytelny.

Często duże firmy używają własnych tak zwanych „reguł programistycznych”.
Przy okazji, najzabawniejszym, ale najmądrzejszym, jaki widziałem, był DRY KISS (Nie powtarzaj się. Keep It Simple, Stupid). :-)

Andrejs Cainikovs
źródło
3

Jak już powiedzieli inni, ważne jest, aby być potocznym (dostosowywać style nazewnictwa i konwencje do kodu, w którym piszesz) i być konsekwentnym.

Przez lata pracowałem na dużej bazie kodu, która używa zarówno konwencji „this->”, jak i notacji podkreślenia postfix dla zmiennych składowych. Przez lata pracowałem również nad mniejszymi projektami, z których niektóre nie miały żadnej konwencji nazewnictwa zmiennych składowych, a inne miały inne konwencje nazywania zmiennych składowych. Spośród tych mniejszych projektów konsekwentnie stwierdzałem, że te, które nie mają żadnej konwencji, są najtrudniejsze do szybkiego wskoczenia i zrozumienia.

Jestem bardzo skłonny do nazywania. Będę się męczyć nad nazwą, która ma być przypisana do klasy lub zmiennej do tego stopnia, że ​​jeśli nie mogę wymyślić czegoś, co uważam za „dobre”, wybiorę nazwę jako coś bezsensownego i przedstawię komentarz opisujący, co to naprawdę jest jest. W ten sposób przynajmniej nazwa oznacza dokładnie to, co mam na myśli - nic dodać nic ująć. I często, po pewnym czasie używania go, odkrywam, jaka naprawdę powinna być nazwa, i mogę wrócić i odpowiednio zmodyfikować lub refaktoryzować.

Ostatnia uwaga na temat IDE wykonującego pracę - to wszystko fajne i dobre, ale IDE często nie są dostępne w środowiskach, w których wykonuję najpilniejszą pracę. Czasami jedyną dostępną rzeczą w tym momencie jest kopia „vi”. Ponadto widziałem wiele przypadków, w których uzupełnianie kodu IDE propagowało głupotę, taką jak niepoprawna pisownia w nazwach. Dlatego wolę nie polegać na kuli IDE.

Chris Cleeland
źródło
3

Pierwotnym pomysłem na prefiksy w zmiennych składowych C ++ było przechowywanie dodatkowych informacji o typie, o których kompilator nie wiedział. Na przykład, możesz mieć łańcuch o stałej długości znaków, a drugi, który jest zmienny i zakończony znakiem „\ 0”. Dla kompilatora są to oba char *, ale jeśli spróbujesz skopiować z jednego do drugiego, wpadniesz w ogromne kłopoty. Więc z czubka mojej głowy

char *aszFred = "Hi I'm a null-terminated string";
char *arrWilma = {'O', 'o', 'p', 's'};

gdzie „asz” oznacza, że ​​ta zmienna jest „ciągiem ascii (zakończonym zerem), a„ arr ”oznacza, że ​​ta zmienna jest tablicą znaków.

Wtedy dzieje się magia. Kompilator będzie doskonale zadowolony z tego stwierdzenia:

strcpy(arrWilma, aszFred);

Ale ty, jako człowiek, możesz spojrzeć na to i powiedzieć „hej, te zmienne nie są tak naprawdę tego samego typu, nie mogę tego zrobić”.

Niestety, wiele miejsc używa standardów, takich jak „m_” dla zmiennych składowych, „i” dla liczb całkowitych, niezależnie od tego, jak są używane, „cp” dla wskaźników znaków. Innymi słowy, powielają to, co wie kompilator, a jednocześnie utrudniają odczytanie kodu. Uważam, że ta zgubna praktyka powinna być prawnie zakazana i podlegać surowym karom.

Na koniec należy wspomnieć o dwóch kwestiach:

  • Rozsądne wykorzystanie funkcji C ++ pozwala kompilatorowi poznać informacje, które trzeba było zakodować w surowych zmiennych w stylu C. Możesz tworzyć klasy, które będą zezwalały tylko na prawidłowe operacje. Należy to zrobić na tyle, na ile jest to praktyczne.
  • Jeśli twoje bloki kodu są tak długie, że można zapomnieć, co wpisać zmienną jest przed użyciem są one sposób zbyt długo. Nie używaj nazw, przeorganizuj.
AL Flanagan
źródło
Przedrostki wskazujące na typ lub rodzaj zmiennej również są warte omówienia, ale odnosiłem się głównie do przedrostków wskazujących, czy coś jest (prywatnym) członkiem / polem. Odwrotna notacja węgierska, o której wspominasz, może być bardzo przydatna, gdy zostanie zastosowana inteligentnie (jak w twoim przykładzie). Moim ulubionym przykładem, w którym ma to sens, są współrzędne względne i bezwzględne. kiedy zobaczysz absX = relX, możesz wyraźnie zobaczyć, że coś może być nie tak. Możesz również odpowiednio nazwać funkcje: absX = absFromRel (relX, offset);
VoidPointer
Uwaga: inicjalizacja aszFred jest wątpliwa (oferuje dostęp inny niż stały do ​​literału), a inicjalizacja arrWilma nawet się nie skompiluje. (Prawdopodobnie zamierzałeś zadeklarować arrWilma jako tablicę zamiast wskaźnika!) Nie ma jednak problemu, ponieważ napisałeś, że jest tuż nad twoją głową ... :-)
Niels Dekker
Ups, masz całkowitą rację. Dzieci, nie próbujcie tego w domu. Zrób to: 'const char * aszFred = "Cześć, jestem łańcuchem zakończonym znakiem null"; char arrWilma [] = {'O', 'o', 'p', 's'}; '
AL Flanagan
3

Nasz projekt zawsze używał „its” jako przedrostka dla danych składowych i „the” jako przedrostka dla parametrów, bez przedrostka dla lokalnych. To trochę fajne, ale zostało przyjęte przez pierwszych programistów naszego systemu, ponieważ widzieli, że jest on używany jako konwencja przez niektóre komercyjne biblioteki źródłowe, których używaliśmy w tamtym czasie (albo XVT, albo RogueWave - może obie). Otrzymasz coś takiego:

void
MyClass::SetName(const RWCString &theName)
{
   itsName = theName;
}

Głównym powodem, dla którego widzę zakres prefiksów (i żadnych innych - nienawidzę notacji węgierskiej), jest to, że zapobiega to wpadaniu w kłopoty, pisząc kod, w którym myślisz, że odnosisz się do jednej zmiennej, ale tak naprawdę odnosisz się do innej zmiennej o tej samej nazwie zdefiniowanej w zakresie lokalnym. Pozwala również uniknąć problemu wymyślania nazw zmiennych, które reprezentowałyby tę samą koncepcję, ale z różnymi zakresami, jak w powyższym przykładzie. W takim przypadku i tak musiałbyś wymyślić jakiś przedrostek lub inną nazwę dla parametru „theName” - czemu nie stworzyć spójnej reguły, która będzie obowiązywać wszędzie.

Samo użycie tego-> nie jest wystarczająco dobre - nie jesteśmy tak zainteresowani zmniejszaniem niejednoznaczności, jak zmniejszaniem błędów w kodowaniu, a maskowanie nazw za pomocą lokalnych identyfikatorów może być uciążliwe. To prawda, niektóre kompilatory mogą mieć opcję zgłaszania ostrzeżeń w przypadkach, w których zamaskowano nazwę w większym zakresie, ale te ostrzeżenia mogą stać się uciążliwe, jeśli pracujesz z dużym zestawem bibliotek innych firm, które akurat wybrały nazwy nieużywanych zmiennych, które czasami kolidują z Twoimi.

A co do samego siebie - szczerze mówiąc, łatwiej mi pisać niż podkreślenia (jako maszynistka dotykowa unikam podkreślenia, gdy tylko jest to możliwe - zbyt duże rozciąganie się od głównych rzędów) i uważam to za bardziej czytelne niż tajemniczy podkreślenie.

Steve Broberg
źródło
To najbardziej intuicyjne rozwiązanie z najszybszą krzywą uczenia się, jaką kiedykolwiek słyszałem. Chciałbym, żeby języki mówione były bardziej elastyczne w obsłudze tych wszystkich, żebyśmy nie musieli myśleć o wymyślaniu nowych technik rozwiązywania niejednoznaczności w kodzie.
Guney Ozsan
2

Używam go, ponieważ Intellisense w VC ++ nie może powiedzieć, kiedy pokazywać prywatnych członków podczas uzyskiwania dostępu poza klasą. Jedynym wskazaniem jest mały symbol „kłódki” na ikonie pola na liście Intellisense. Po prostu ułatwia identyfikację prywatnych członków (pól). Szczerze mówiąc, nawyk z C #.

class Person {
   std::string m_Name;
public:
   std::string Name() { return m_Name; }
   void SetName(std::string name) { m_Name = name; }
};

int main() {
  Person *p = new Person();
  p->Name(); // valid
  p->m_Name; // invalid, compiler throws error. but intellisense doesn't know this..
  return 1;
}
Zack
źródło
2

Myślę, że jeśli potrzebujesz prefiksów, aby odróżnić członków klasy od parametrów funkcji składowych i zmiennych lokalnych, albo funkcja jest zbyt duża, albo zmienne są źle nazwane. Jeśli nie mieści się na ekranie, możesz łatwo zobaczyć, co jest czym, refaktoryzuj.

Biorąc pod uwagę, że często są deklarowane z dala od miejsca, w którym są używane, uważam, że konwencje nazewnictwa dla stałych globalnych (i zmiennych globalnych, chociaż IMO rzadko kiedy istnieje potrzeba ich użycia) mają sens. Ale poza tym nie widzę dużej potrzeby.

To powiedziawszy, zwykłem umieszczać podkreślenie na końcu wszystkich członków klasy prywatnej. Ponieważ wszystkie moje dane są prywatne, oznacza to, że członkowie mają na końcu podkreślenie. Zwykle już tego nie robię w nowych bazach kodu, ale ponieważ jako programista pracujesz głównie ze starym kodem, nadal często to robię. Nie jestem pewien, czy moja tolerancja dla tego nawyku wynika z tego, że robiłem to zawsze i nadal robię to regularnie, czy naprawdę ma to więcej sensu niż oznaczanie zmiennych składowych.

sbi
źródło
2
To bardzo dobrze odzwierciedla moje odczucia dotyczące tej kwestii. Kod powinien być czytelny bez uciekania się do przedrostków. Może nie widzimy tak wielu prefiksów w nowocześniejszych językach, ponieważ ich społeczności użytkowników przyjęły czytelność nieco bardziej niż to, co czasami widzisz w C ++. Oczywiście C ++ może i powinien być czytelny. Po prostu przez lata napisano wiele nieczytelnego C ++.
VoidPointer
2

W Pythonie wiodące podwójne podkreślenia są używane do emulacji prywatnych członków. Aby uzyskać więcej informacji, zobacz tę odpowiedź

Konstantin Tenzin
źródło
1

Ze względu na zarządzanie pamięcią przydatne jest rozróżnianie między zmiennymi składowymi i zmiennymi lokalnymi. Mówiąc ogólnie, zmienne składowe przydzielone do sterty powinny zostać zniszczone w destruktorze, podczas gdy zmienne lokalne przydzielone do sterty powinny zostać zniszczone w tym zakresie. Zastosowanie konwencji nazewnictwa do zmiennych składowych ułatwia prawidłowe zarządzanie pamięcią.

Frankster
źródło
jak to? Destruktor nie ma dostępu do zmiennych lokalnych zadeklarowanych w innych funkcjach, więc nie ma tu miejsca na zamieszanie. Poza tym zmienne lokalne alokowane na stercie nie powinny istnieć . Zmienne składowe alokowane na stercie powinny istnieć tylko wewnątrz klas RAII.
jalf
„Zmienne lokalne przydzielone do sterty nie powinny istnieć” jest trochę mocne. Ale jeśli / kiedy ich używasz, bardzo ważne jest, aby upewnić się, że zostaną prawidłowo zwolnione, więc zdyscyplinowana konwencja nazewnictwa dla zmiennych składowych w porównaniu ze zmiennymi lokalnymi niezmiernie pomaga w zapewnieniu tego.
frankster
1

Code Complete zaleca m_varname jako zmienne składowe.

Chociaż nigdy nie sądziłem, że notacja m_ jest przydatna, przy tworzeniu standardu nadałbym opinii McConnellowi wagę.

Paul Nathan
źródło
2
Nie, chyba że wyjaśni, dlaczego podkreślenie. Jestem wielkim fanem jego książki „Rapid Development”, którą tu wielokrotnie polecałem, ale znacznie mniej „Code Complete” (którego, przyznaję, nie czytałem od czasu jej wydania).
1

Prawie nigdy nie używam przedrostków przed nazwami moich zmiennych. Jeśli używasz wystarczająco przyzwoitego IDE, powinieneś być w stanie łatwo refaktoryzować i znajdować odniesienia. Używam bardzo jasnych nazw i nie boję się mieć długich nazw zmiennych. Z tą filozofią też nigdy nie miałem problemów z zakresem.

Jedyny raz, kiedy używam prefiksu, byłby w wierszu podpisu. Przedrostuję parametry do metody znakiem _, aby móc programować wokół nich defensywnie.

James
źródło
1

Nigdy nie powinieneś potrzebować takiego przedrostka. Jeśli taki przedrostek oferuje jakąkolwiek korzyść, Twój styl kodowania ogólnie wymaga naprawy i to nie prefiks sprawia, że ​​kod nie jest przejrzysty. Typowe złe nazwy zmiennych to „inne” lub „2”. Nie naprawiasz tego wymagając, aby była mOther, naprawiasz to, zmuszając programistę do przemyślenia tego, co robi ta zmienna w kontekście tej funkcji. Być może miał na myśli remoteSide, newValue, secondTestListener lub coś w tym zakresie.

To skuteczny anachronizm, który wciąż jest zbyt daleko rozpowszechniany. Przestań poprzedzać zmienne i nadaj im nazwy własne, których przejrzystość odzwierciedla czas ich używania. Maksymalnie 5 linii można nazwać „i” bez zamieszania; poza 50 liniami potrzebujesz dość długiej nazwy.

dascandy
źródło
1

Lubię nazwy zmiennych, aby nadawać znaczenie tylko wartościom, które zawierają, i pozostawić sposób ich zadeklarowania / implementacji poza nazwą. Chcę wiedzieć, co oznacza wartość, kropka. Może zrobiłem więcej niż przeciętną ilość refaktoryzacji, ale uważam, że osadzenie sposobu, w jaki coś jest zaimplementowane w nazwie, sprawia, że ​​refaktoryzacja jest bardziej żmudna, niż powinna. Prefiksy wskazujące, gdzie lub jak są deklarowane elementy składowe obiektu, są specyficzne dla implementacji.

color = Red;

Przez większość czasu nie obchodzi mnie, czy czerwony jest wyliczeniem, strukturą lub czymkolwiek, a jeśli funkcja jest tak duża, że ​​nie pamiętam, czy kolor został zadeklarowany lokalnie, czy jest składnikiem, prawdopodobnie czas na przerwanie funkcję na mniejsze jednostki logiczne.

Jeśli twoja cykliczna złożoność jest tak duża, że ​​nie możesz śledzić tego, co dzieje się w kodzie bez wskazówek specyficznych dla implementacji osadzonych w nazwach rzeczy, najprawdopodobniej musisz zmniejszyć złożoność swojej funkcji / metody.

Przeważnie używam „this” tylko w konstruktorach i inicjatorach.

ChrisG65
źródło
0

Używam m_ do zmiennych składowych, aby skorzystać z Intellisense i powiązanych funkcji IDE. Kiedy koduję implementację klasy, mogę wpisać m_ i zobaczyć combobox ze wszystkimi członkami m_ zgrupowanymi razem.

Ale mógłbym żyć bez m_'s, oczywiście. To tylko mój styl pracy.

Hernán
źródło
Możesz także wpisaćthis->
Toast
0

Według JOINT STRIKE FIGHTER AIR VEHICLE C ++ STANDARDS (grudzień 2005):

Reguła AV 67

Dane publiczne i chronione powinny być używane tylko w strukturach, a nie w klasach. Uzasadnienie: Klasa jest w stanie zachować niezmienność, kontrolując dostęp do swoich danych. Jednak klasa nie może kontrolować dostępu do swoich członków, jeśli te elementy nie są prywatne. Dlatego wszystkie dane w klasie powinny być prywatne.

W związku z tym przedrostek „m” staje się bezużyteczny, ponieważ wszystkie dane powinny być prywatne.

Ale dobrym zwyczajem jest używanie przedrostka p przed wskaźnikiem, ponieważ jest to niebezpieczna zmienna.

Pierre-Louis Deschamps
źródło
0

Wiele z tych konwencji pochodzi z czasów bez wyrafinowanych edytorów. Poleciłbym użycie odpowiedniego IDE, które pozwala na pokolorowanie każdego rodzaju zmiennej. Kolor jest zdecydowanie łatwiejszy do wykrycia niż jakikolwiek prefiks.

Jeśli potrzebujesz uzyskać jeszcze więcej szczegółów na temat zmiennej, każde nowoczesne IDE powinno być w stanie pokazać ci ją, przesuwając kursor lub kursor na nią. A jeśli użyjesz zmiennej w niewłaściwy sposób (na przykład wskaźnika z operatorem.), I tak otrzymasz błąd.

Elias Mueller
źródło