Объяснение использования C ++ Const

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

Может кто-нибудь объяснить использование каждой из констант?

RoR
источник
27
Мне очень нравится этот метод расшифровки сложных объявлений: c-faq.com/decl/spiral.anderson.html
Джейсон

Ответы:

77

Прочтите это: https://isocpp.org/wiki/faq/const-correctness

Финал const означает, что функция Method3не изменяет неизменяемые члены своего класса.

const int* const означает постоянный указатель на константу int: то есть указатель, который не может быть изменен, на int, который не может быть изменен: единственная разница между this и const int& том, что он может бытьnull

const int* const&означает ссылку на постоянный указатель на константу типа int. Обычно указатели не передаются по ссылке; const int* &имеет больше смысла, потому что это будет означать, что указатель может быть изменен во время вызова метода, что было бы единственной причиной, по которой я мог бы передать указатель по ссылке, const int* const&во всех смыслах и целях одинаково, const int* constза исключением того, что он, вероятно, менее эффективен поскольку указатели представляют собой простые старые типы данных (POD), и они, как правило, должны передаваться по значению.

сатнхак
источник
104

Легче понять, если вы перепишете это как полностью эквивалентный

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

затем прочтите ее справа налево.

№5 говорит, что все объявление функции слева есть const, что означает, что это обязательно функция-член, а не бесплатная функция.

№4 говорит, что указатель слева есть const(не может быть изменен, чтобы указывать на другой адрес).

№3 говорит, что intслева есть const(не может быть изменено на другое значение).

№2 говорит, что указатель слева есть const.

№1 говорит, что intслева есть const.

Собрав все это вместе, вы можете прочитать это как функцию- constчлен с именем, Method3которая принимает ссылку на constуказатель на int const(или const int, если хотите) и возвращает constуказатель на int const(const int ).

(Nb # 2 совершенно излишне .)

ildjarn
источник
22

Прежде всего const TэквивалентенT const .

const int* const поэтому эквивалентно int const * const .

При чтении выражений с большим количеством constтокенов и указателей всегда старайтесь читать их справа налево (после применения преобразования, описанного выше). Итак, в этом случае возвращаемое значение является указателем const на константуint . Создание самого указателя constздесь не имеет смысла, поскольку возвращаемое значение не является lvalue, которое можно изменить. Однако создание указателя constгарантирует, что вызывающий не может изменять int(или массив ints), возвращаемыйMethod3 .

const int*const&становится int const*const&, поэтому это ссылка на константный указатель на константуint . Передача константного указателя по ссылкам male тоже не имеет смысла - вы не можете изменить указанное значение, поскольку указатель есть, constа ссылки и указатели занимают одинаковую память, поэтому нет никакой экономии места.

Последнее constуказывает на то, что метод не изменяет thisобъект. thisУказатель внутри тела метода будет иметь (теоретически) заявление T const * const this. Это означает, что const T*объект сможет вызывать T::Method3().

Александр Гесслер
источник
2
Голосование за это (и аналогичный ответ ildjarn), отчасти для того, чтобы подчеркнуть, что все это имеет больше смысла, если вы не помещаете первые consts в начало фразы. Именно поэтому я считаю, что это плохая практика const, даже если язык позволяет это, и это наиболее распространенное использование.
TED
12

Легкий способ запомнить правила const- это думать об этом так: constприменимо к объекту слева от него, если слева от него ничего нет.

Итак, в случае const int * constс первой константой слева ничего нет, поэтому она применяется к, intа вторая имеет что-то слева, поэтому она применяется к указателю.

Это правило также говорит вам, что произойдет в том случае, если у вас есть const int const *. Поскольку обе константы применяются к intэтому выражению, это избыточно и поэтому недействительно.

Йони
источник
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 */
Джастин
источник
3

Мне нравится использовать метод «часы» или «спираль», когда, начиная с имени идентификатора (в данном случае Method3), вы читаете туда и обратно слева направо, назад налево и т. Д., Чтобы декодировать соглашения об именах. Так const int* const Method3(const int* const&) constчто это метод класса, который не изменяет никаких членов класса (некоторого безымянного класса) и принимает постоянную ссылку на указатель, который указывает на константу, intи возвращает постоянный указатель на константу.int .

Надеюсь это поможет,

Джейсон

Джейсон
источник
2

Самый простой способ запомнить константу в C ++ - это увидеть код в такой форме:

XXX const;
const YYY;

XXX, YYY будет постоянным компонентом,
XXX constформа:

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

const YYY форма:

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

Обычно люди используют эти типы. Когда вы что- "const&"то видите , не пугайтесь, const что-то описывает перед собой. так что ответ на эту проблему теперь очевиден.

const int* const Method3(const int* const&) const;
  |          |             |          |       |
  #3         #2            #3         #2      #1
Альберт Чен
источник
2

Я только хочу упомянуть, что const int* const&это действительно постоянная ссылка на const int*. Например:

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'

Это также относится к int* const&, Что означает: «Постоянная ссылка на int*».
Но const int*&это непостоянная ссылка на const int*.
Надеюсь это поможет.

MrDetective
источник
1

Чтение справа налево упрощает понимание модификаторов.

Метод const, который принимает ссылку на указатель const на вызываемый const int, Method3который возвращает указатель const на const int.

  1. Метод const не может изменять члены (если они явно не указаны mutable)
  2. Константный указатель нельзя изменить, чтобы указать на что-то еще
  3. Const int (или любой другой тип) не может быть изменен
Ник Струпат
источник
1

const # 1: указатель, возвращаемый Method3, ссылается на const int.

const # 2: значение указателя, возвращаемое самой функцией, равно const. Это бесполезная константа (хотя грамматически правильная), потому что возвращаемое значение из функции не может быть l-значением.

const # 3: Тип указателя, переданный по ссылке в функцию, указывает на const int.

const # 4: значение указателя, переданное по ссылке в функцию, само является константным указателем. Объявление значения, которое передается функции как const, обычно бессмысленно, но это значение передается по ссылке, поэтому оно может иметь смысл.

const # 5: функция (предположительно, функция-член) является константой, что означает, что ей не разрешено (а) назначать новые значения любым членам объекта, частью которого она является, или (б) вызывать неконстантную функцию-член на объекте или на любом из его членов.

Джоллиморфный
источник
0
  • const в конце метода стоит квалификатор, означающий, что состояние объекта не будет изменено.

  • const int*const&означает получение по ссылке константного указателя на константное местоположение. Он не может ни указывать на другое местоположение, ни изменять значение, на которое указывает.

  • const int*const - это возвращаемое значение, которое также является постоянным указателем на постоянное местоположение.

Махеш
источник
0

Несколько примеров могут быть хороши для демонстрации этой концепции, чем больше, тем лучше 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;
   }
};

Надеюсь, это поможет!

Растус7
источник