Na początek prawdopodobnie wiesz, że const
można to wykorzystać do uczynienia danych obiektu lub wskaźnika niemodyfikowalnymi lub obu.
const Object* obj; // can't change data
Object* const obj; // can't change pointer
const Object* const obj; // can't change data or pointer
Możesz jednak również użyć składni:
Object const *obj; // same as const Object* obj;
Jedyne, co wydaje się mieć znaczenie, to po której stronie gwiazdki umieścisz const
słowo kluczowe. Osobiście wolę umieścić const
po lewej stronie typu, aby określić, że dane nie są modyfikowalne, ponieważ uważam, że lepiej czyta się w moim nastawieniu od lewej do prawej, ale która składnia była pierwsza?
Co ważniejsze, dlaczego istnieją dwa prawidłowe sposoby określania const
danych i w jakiej sytuacji wolałbyś lub potrzebowałbyś jednego nad drugim, jeśli w ogóle?
Edytować:
Wygląda więc na to, że była to arbitralna decyzja, kiedy standard dotyczący tego, jak kompilatorzy powinni interpretować rzeczy, został opracowany na długo przed moim urodzeniem. Ponieważ const
jest stosowane do tego, co znajduje się po lewej stronie słowa kluczowego (domyślnie?), Domyślam się, że nie było nic złego w dodaniu „skrótów” do stosowania słów kluczowych i kwalifikatorów typów w inny sposób, przynajmniej do czasu, gdy deklaracja zmieni się o analizowanie * lub & ...
Tak było również w C, więc zakładam?
const
po typie, np.#define MAKE_CONST(T) T const
Zamiast#define MAKE_CONST(T) const T
tak,MAKE_CONST(int *)
aby poprawnie rozwinął się doint * const
zamiastconst int *
.Odpowiedzi:
Zasadniczo powodem, dla którego pozycja
const
wewnątrz specyfikatorów przed gwiazdką nie ma znaczenia, jest to, że gramatyka języka C została zdefiniowana w ten sposób przez Kernighana i Ritchiego.Powodem, dla którego zdefiniowali gramatykę w ten sposób, było prawdopodobne, że ich kompilator C przeanalizował dane wejściowe od lewej do prawej i zakończył przetwarzanie każdego tokenu, gdy je zużywał. Użycie
*
tokenu zmienia stan bieżącej deklaracji na typ wskaźnika. Spotkanieconst
po*
oznacza, żeconst
kwalifikator jest stosowany do deklaracji wskaźnika; napotykając go przed*
środkami, kwalifikator jest stosowany do wskazanych danych.Ponieważ znaczenie semantyczne nie zmienia się, jeśli
const
kwalifikator pojawia się przed lub po specyfikatorach typu, jest akceptowany w obu przypadkach.Podobny przypadek pojawia się przy deklarowaniu wskaźników do funkcji, gdzie:
void * function1(void)
deklaruje funkcję, która zwracavoid *
,void (* function2)(void)
deklaruje wskaźnik funkcji do funkcji, która zwracavoid
.Znowu należy zauważyć, że składnia języka obsługuje parser od lewej do prawej.
źródło
*
parser kompilatora nie wie, że jest wskaźnikiem, jest to stała dla wartości danych. Po * jest to związane ze stałym wskaźnikiem. Znakomity. I wreszcie wyjaśnia, dlaczego mogę zrobićconst char
równie dobrzechar const
.const
aby zawsze pojawiał się po rzeczy, do której się kwalifikuje ... dokładnie tak, abym zawsze mógł zakończyć przetwarzanie const natychmiast po jej spożyciu. Wydaje się więc, że jest to argument przemawiający za zakazem zachodu, zamiast na to zezwalać.++
operator? „To zdanie”, imho, pomogło mi zrozumieć, że nie ma żadnego konkretnego powodu poza tym, że mogli. Może dziś dokonaliby innego wyboru, a może nie.Zasada jest taka:
Wolę używać const po prawej stronie rzeczy, aby być const tylko dlatego, że jest to „oryginalny” sposób definiowania const.
Myślę jednak, że jest to bardzo subiektywny punkt widzenia.
źródło
Object const *
jest wskaźnikiem do stałej Object. Jeśli umieściszconst
po lewej stronie, odczytałby on jako wskaźnik do obiektu, który jest stałą, która tak naprawdę nie płynie zbyt dobrze.const
nie jest to klasa pamięci, ale ludzie nie są parserami).Wolę drugą składnię. Pomaga mi śledzić to, co jest stałe, czytając deklarację typu od prawej do lewej:
źródło
Object const* const
, nieconst const Object*
. „const” nie może znajdować się po lewej stronie, z wyjątkiem szczególnych przypadków, w których tak wielu ludzi absolutnie je uwielbia. (Zobacz Heath powyżej.)Kolejność słów kluczowych w deklaracji nie jest ustalona. Istnieje wiele alternatyw dla „jedynego prawdziwego porządku”. Lubię to
czy powinno być
??
źródło
decl-specifier-seq
jest sekwencjądecl-specifier
s. W gramatyce nie ma kolejności, a liczba wystąpień każdego słowa kluczowego jest ograniczona tylko pewnymi regułami semantycznymi (możesz mieć jedną,const
ale dwielong
:-)unsigned
to typ, taki sam jakunsigned int
iint unsigned
.unsigned long
to inny typ, taki sam jakunsigned long int
iint long unsigned
. Widzisz wzór?;)
. OK, dziękistatic
mogłeś dodawać do tej gmatwaniny słów, ale dopiero niedawno kompilatorzy narzekali, żestatic
musi to być pierwsze.Pierwszą zasadą jest użycie dowolnego formatu wymaganego przez lokalne standardy kodowania. Po tym: umieszczenie początku
const
na początku prowadzi do niekończącego się zamieszania, gdy w grę wchodzą kroje pisma, np .:Jeśli twój standard kodowania zezwala na typedef wskaźników, to naprawdę powinien nalegać na umieszczenie stałej po typie. W każdym przypadku, ale w przypadku zastosowania do typu, const musi być zgodne z tym, do czego się odnosi, więc spójność również przemawia na korzyść stałej po. Ale lokalne wytyczne dotyczące kodowania mają przewagę nad wszystkimi tymi; różnica zwykle nie jest na tyle ważna, aby wrócić i zmienić cały istniejący kod.
źródło
const std::string::const_iterator
;)const IntPtr p1
oznaczać inne niż „stały wskaźnik liczby całkowitej” (tj. „Stały wskaźnik do liczby całkowitej”)? Nikt przy zdrowych zmysłach, nawet nie wiedząc, jakIntPtr
jest zdefiniowane, nie pomyślałby, żep1
jest to zmienne. A jeśli o to chodzi, dlaczego ktoś miałby błędnie zakładać, że*p1
jest to niezmienne? Co więcej, umieszczenie const gdziekolwiek indziej (np.IntPtr const p1
) W ogóle nie zmienia semantyki.std::vector<T>::const_iterator
, w których to nie iterator jest stałą, ale wskazuje).Istnieją historyczne powody, dla których lewica lub prawica są dopuszczalne. Stroustrup dodał const do C ++ w 1983 roku , ale nie dotarł do C aż do C89 / C90.
W C ++ jest dobry powód, aby zawsze używać const po prawej stronie. Będziesz spójny wszędzie, ponieważ funkcje składowe const muszą być deklarowane w ten sposób:
źródło
const int& getInt();
int& const getInt();
int const& getInt();
jest lepszy niż odpowiednik,const int& getInt();
podczas gdyint& const getInt();
to, z którym go porównujesz, jest zbędny (odniesienia są już stałe), chociaż legalne i zwykle daje ostrzeżenie. W każdym razie const na funkcji składowej zmieniathis
wskaźnik w funkcji zFoo* const
naFoo const* const
.const
funkcja składowa nie oznacza wcale tego samego - czy też jestvoid set(int)&;
jakimś odniesieniem do funkcji?