Objaśnienie użycia C ++ Const

97
const int* const Method3(const int* const&) const;

Czy ktoś może wyjaśnić użycie każdej z const?

RoR
źródło
27
Bardzo podoba mi się ta metoda odszyfrowywania skomplikowanych deklaracji: c-faq.com/decl/spiral.anderson.html
Jason

Odpowiedzi:

77

Przeczytaj to: https://isocpp.org/wiki/faq/const-correctness

Ostatnia constoznacza, że ​​funkcja Method3nie modyfikuje niezmiennych elementów swojej klasy.

const int* constoznacza stały wskaźnik do stałej int: tj. wskaźnik, którego nie można zmienić, do int, którego nie można zmienić: jedyna różnica między tym a const int&polega na tym, że może byćnull

const int* const&oznacza odniesienie do stałego wskaźnika do stałej int. Zazwyczaj wskaźniki nie są przekazywane przez odniesienie; const int* &ma więcej sensu, ponieważ oznaczałoby to, że wskaźnik można zmienić podczas wywołania metody, co byłoby jedynym powodem, dla którego widzę, aby przekazać wskaźnik przez odniesienie, const int* const&jest do wszystkich intencji i celów takie same, const int* constz wyjątkiem tego, że jest prawdopodobnie mniej wydajne Ponieważ wskaźniki są zwykłymi starymi typami danych (POD) i generalnie powinny być przekazywane przez wartość.

satnhak
źródło
104

Łatwiej jest zrozumieć, jeśli przepiszesz to jako całkowicie równoważne

// v───v───v───v───v───v───v───v───v───v───v───v─┬┐
//                                               ││
//  v──#1    v─#2             v──#3    v─#4      #5
   int const * const Method3(int const * const&) const;

następnie przeczytaj go od prawej do lewej.

# 5 mówi, że cała deklaracja funkcji po lewej stronie to const, co oznacza, że ​​jest to koniecznie funkcja składowa, a nie funkcja wolna.

# 4 mówi, że wskaźnik po lewej stronie to const(nie można go zmienić, aby wskazywał na inny adres).

# 3 mówi, że po intlewej stronie jest const(nie można go zmienić, aby miał inną wartość).

# 2 mówi, że wskaźnik po lewej to const.

# 1 mówi, że intpo lewej stronie jest const.

Łącząc to wszystko razem, możesz to odczytać jako constfunkcję składową Method3o nazwie, która pobiera odwołanie do constwskaźnika do int const(lub const int, jeśli wolisz) i zwraca constwskaźnik do int const( const int).

(Uwaga nr 2 jest całkowicie zbędna ).

ildjarn
źródło
22

Przede wszystkim const Tjest odpowiednikiem T const.

const int* constjest zatem równoważne z int const * const.

Czytając wyrażenia z dużą ilością consttokenów i wskaźników, zawsze staraj się czytać je od prawej do lewej (po zastosowaniu powyższej transformacji). W tym przypadku wartość zwracana jest wskaźnikiem stałej do stałejint . Samo tworzenie wskaźnika nie constma tutaj sensu, ponieważ zwracana wartość nie jest lwartością, którą można by zmodyfikować. Wskazanie constjednak gwarantuje, że wywołujący nie może modyfikować int(lub tablicy ints) zwróconej przez Method3.

const int*const&staje się int const*const&, więc jest to odniesienie do stałej wskaźnika do stałejint . Przekazywanie wskaźnika do stałej przez referencje męskie również nie ma sensu - nie możesz modyfikować wartości, do której się odwołujesz, ponieważ wskaźnik jest, consta referencje i wskaźniki zajmują taką samą pamięć, więc nie ma również żadnych oszczędności miejsca.

Ostatnia constwskazuje, że metoda nie modyfikuje thisobiektu. thisWskaźnik w organizmie metoda będzie miał (teoretyczna) oświadczenie T const * const this. Oznacza to, że const T*obiekt będzie mógł wywołać T::Method3().

Alexander Gessler
źródło
2
Głosowanie nad tym (i podobną odpowiedzią ildjarna), częściowo po to, aby podkreślić, że całość ma więcej sensu, jeśli nie umieścisz pierwszych liter na początku constfrazy. Właśnie dlatego uważam, że umieszczanie go w tym miejscu jest złą praktyką const, mimo że język na to pozwala, i jest to najpowszechniejsze użycie.
TED
12

Łatwym sposobem na zapamiętanie zasad programu constjest myślenie o tym w ten sposób: constdotyczy rzeczy po lewej stronie, chyba że nic nie ma po lewej stronie.

Tak więc w przypadku const int * constpierwszej stałej nie ma nic po lewej stronie, więc odnosi się do, inta druga ma coś po lewej stronie, więc odnosi się do wskaźnika.

Ta reguła mówi również, co by się stało, gdybyś miał const int const *. Ponieważ obie const mają zastosowanie do inttego wyrażenia, jest zbędne, a zatem nieważne.

Yony
źródło
3
const /* don't modify the int or array of ints' value(s) */
int* const /* as a retval, ignored. useless declaration */
Method3(const /* don't modify the int or array of ints' value(s) */
int* const /* don't modify the pointer's value, the address to which `pointer` points to. e.g. you cannot say `++pointer` */
&) const; /* this method does not modify the instance/object which implements the method */
Justin
źródło
3

Lubię używać metody "zegara" lub "spirali", gdzie zaczynając od nazwy identyfikatora (w tym przypadku Method3) czytasz tam iz powrotem od lewej do prawej, z powrotem do lewej itd. W celu dekodowania konwencje nazewnictwa. const int* const Method3(const int* const&) constJest to więc metoda klasy, która nie zmienia żadnych składowych klasy (jakiejś nienazwanej klasy) i przyjmuje stałe odwołanie do wskaźnika, który wskazuje na stałą, inti zwraca stały wskaźnik do stałej int.

Mam nadzieję że to pomoże,

Jason

Jason
źródło
2

Łatwym sposobem na zapamiętanie stałej w C ++ jest widok kodu w postaci:

XXX const;
const YYY;

XXX, YYY będzie składnikiem stałym w
XXX constpostaci:

function ( def var ) const;    ------#1
* const;                       ------#2

const YYY Formularz:

const int;                     ------#3
const double;

Ludzie zwykle używają tych typów. Kiedy "const&"gdzieś widzisz , nie czuj się zdezorientowany, const opisuje coś przed sobą. więc odpowiedź na ten problem jest teraz oczywista.

const int* const Method3(const int* const&) const;
  |          |             |          |       |
  #3         #2            #3         #2      #1
Albert Chen
źródło
2

Chcę tylko wspomnieć, że const int* const&jest to rzeczywiście stałe odniesienie do const int*. Na przykład:

int i = 0;
int j = 1;
int* p = &i;
int* q = &j;
const int* const& cpref = p;
cpref = q; //Error: assignment of read-only reference 'cpref'

Dotyczy to również int* const&, co oznacza: „Stałe odniesienie do int*”.
Ale const int*&jest to niestałe odniesienie do const int*.
Mam nadzieję że to pomoże.

MrDetective
źródło
1

Czytanie od prawej do lewej ułatwia zrozumienie modyfikatorów.

Metoda const, która przyjmuje odwołanie do stałego wskaźnika do stałej int o nazwie, Method3która zwraca wskaźnik do stałej wartości stałej int.

  1. Metoda const nie może modyfikować członków (chyba że są jawnie mutable)
  2. Nie można zmienić wskaźnika do stałej, aby wskazywał na coś innego
  3. Nie można modyfikować const int (ani żadnego innego typu)
Nick Strupat
źródło
1

const # 1: wskaźnik zwrócony przez Method3 odnosi się do stałej int.

const # 2: Wartość wskaźnika zwracana przez samą funkcję to const. Jest to bezużyteczna stała (choć poprawna gramatycznie), ponieważ wartość zwracana przez funkcję nie może być wartością l.

const # 3: typ wskaźnika przekazany przez odniesienie do funkcji wskazuje na stałą int.

const # 4: Wartość wskaźnika przekazana przez odniesienie do funkcji jest sama w sobie wskaźnikiem do stałej. Deklarowanie wartości, która jest przekazywana do funkcji jako const normalnie byłoby bezcelowe, ale ta wartość jest przekazywana przez odwołanie, więc może mieć znaczenie.

const # 5: Funkcja (prawdopodobnie funkcja składowa) jest stała, co oznacza, że ​​nie można (a) przypisywać nowych wartości żadnym członom obiektu, którego jest częścią lub (b) wywoływać funkcji niebędącej składową stałą na obiekcie lub którymkolwiek z jego członków.

Jollymorphic
źródło
0
  • const na końcu metody znajduje się kwalifikator oznaczający, że stan obiektu nie zostanie zmieniony.

  • const int*const&oznacza otrzymywanie przez odwołanie wskaźnika do stałej lokalizacji. Nie może się zmienić, aby wskazywać inną lokalizację, ani zmienić wartości, na którą wskazuje.

  • const int*const jest wartością zwracaną, która jest również stałym wskaźnikiem do stałej lokalizacji.

Mahesh
źródło
0

Kilka przykładów może być fajnych, aby zademonstrować tę koncepcję, im więcej, tym lepsze imho.

class TestClass
{
private:
   int iValue;
   int* oValuePtr;
   int& oValueRef;

public:
   int TestClass::ByValMethod1(int Value)
   {
      // Value can be modified
      Value++;

      // iValue can be modified
      iValue = Value;
      iValue += 1;

      // Return value can be modified
      return ++iValue;
   }

   int TestClass::ByValMethod2(const int Value)
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // iValue can be modified
      iValue = Value;
      iValue += 1;

      // Return value can be modified
      return ++iValue;
   }

   const int TestClass::ByValMethod3(int Value)
   {
      // Value can be modified
      Value++;

      // iValue can be modified
      iValue = Value;
      iValue += 1;

      // Return value can be modified
      return ++iValue;
   }

   const int TestClass::ByValMethod4(const int Value)
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // iValue can be modified
      iValue = Value;
      iValue += 1;

      // Return value can be modified
      return ++iValue;
   }

   const int TestClass::ByValMethod5(const int Value) const
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // iValue *cannot* be modified
      // Access through a const object
      iValue = Value;
      iValue += 1;

      // Return value *cannot* be modified
      // Access through a const object
      return ++iValue;
   }

   int& TestClass::ByRefMethod1(int& Value)
   {
      // Value can be modified
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   int& TestClass::ByRefMethod2(const int& Value)
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   const int& TestClass::ByRefMethod3(int& Value)
   {
      // Value can be modified
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   const int& TestClass::ByRefMethod4(const int& Value)
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   const int& TestClass::ByRefMethod5(const int& Value) const
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   int* TestClass::PointerMethod1(int* Value)
   {
      // Value can be modified
      Value++;

      // oValuePtr can be assigned
      oValuePtr = Value;

      // oValuePtr can be modified
      oValuePtr += 1;

      // Return value can be modified
      return ++oValuePtr;
   }

   int* TestClass::PointerMethod2(const int* Value)
   {
      // Value can be modified
      Value++;

      // oValuePtr cannot be assigned
      // const int* to int*
      oValuePtr = Value;

      // oValuePtr can be modified
      oValuePtr += 1;

      // Return value can be modified
      return ++oValuePtr;
   }

   const int* TestClass::PointerMethod3(int* Value)
   {
      // Value can be modified
      Value++;

      // oValuePtr can be assigned
      oValuePtr = Value;

      // iValue can be modified
      oValuePtr += 1;

      // Return value can be modified
      return ++oValuePtr;
   }

   const int* TestClass::PointerMethod4(const int* Value)
   {
      // Value cannot be modified
      Value++;

      // oValuePtr *cannot* be assigned
      // const int* to int*
      oValuePtr = Value;

      // oValuePtr can be modified
      oValuePtr += 1;

      // Return value can be modified
      return ++oValuePtr;
   }

   const int* TestClass::PointerMethod5(const int* Value) const
   {
      // Value can be modified
      ++Value;

      // oValuePtr *cannot* be assigned
      // const int* to int* const
      // Access through a const object
      oValuePtr = Value;

      // oValuePtr *cannot* be modified
      // Access through a const object
      oValuePtr += 1;

      // Return value *cannot* be modified
      return ++oValuePtr;
   }
};

Mam nadzieję, że to pomoże!

Rastus7
źródło