Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

В C , C ++ , D , JavaScript и Джулии языков программирования , сопзЬ является тип Классификатор : [а] ключевое слово применяется к типу данных , который указывает , что данные только для чтения. Хотя это можно использовать для объявления констант , constв семействе языков C отличается от аналогичных конструкций в других языках тем, что он является частью типа и, следовательно, имеет сложное поведение в сочетании с указателями , ссылками, составными типами данных и проверкой типов.

Введение [ править ]

При применении в качестве объекта декларации , [Ь] это указывает на то, что объект является постоянной : его значение не может быть изменено, в отличие от переменной . Это основное использование - объявление констант - имеет параллели во многих других языках.

Однако, в отличие от других языков, в семействе языков C constявляется частью типа , а не частью объекта . Например, в C, объявляет объект из типа - является частью типа, как если бы это были разобраны «(Int Const) х» - а в Ada , объявляет константу (вид объекта) из типа: это часть объекта , но не часть типа .int const x = 1;xint constconstX : constant INTEGER := 1_XINTEGERconstant

Это дает два тонких результата. Во-первых, constможет применяться к частям более сложного типа - например, int const * const x;объявляет постоянный указатель на постоянное целое число, в то время как int const * x;объявляет переменный указатель на постоянное целое число и int * const x;объявляет постоянный указатель на переменное целое число. Во-вторых, поскольку он constявляется частью типа, он должен соответствовать как часть проверки типа. Например, следующий код недействителен:

void  f ( int &  x ); // ... int  const  i ; f ( i );

потому что аргумент fдолжен быть переменным целым числом, но iявляется постоянным целым числом. Это соответствие является формой корректности программы и известно как константная корректность . Это позволяет использовать форму программирования по контракту , в которой функции указывают как часть своей сигнатуры типа , изменяют ли они свои аргументы или нет, а также может ли их возвращаемое значение изменять или нет. Эта проверка типов в первую очередь интересна для указателей и ссылок - а не для базовых типов значений, таких как целые числа, - но также для составных типов данных или шаблонных типов, таких как контейнеры . Это скрыто тем, чтоconstчасто может быть опущен из-за приведения типов (неявное преобразование типов ) и из-за того, что C является вызовом по значению (C ++ и D являются либо вызовом по значению, либо вызовом по ссылке).

Последствия [ править ]

Идея постоянства не подразумевает, что переменная, хранящаяся в памяти компьютера, не может быть записана. Скорее const-ness - это конструкция времени компиляции, которая указывает, что программист должен делать, не обязательно то, что он может делать. Однако следует отметить, что в случае предварительно определенные данные (например, char const * строковые литералы ), С constявляется часто недоступен для записи.

Отличие от констант [ править ]

Хотя константа не меняет своего значения во время работы программы, объявленный объект constдействительно может изменить свое значение во время работы программы. Типичный пример - регистры только для чтения во встроенных системах, такие как текущее состояние цифрового входа. Регистры данных для цифровых входов часто обозначаются как constи volatile. Содержимое этих регистров может измениться без каких-либо действий программы ( volatile), но вы также не должны писать в них ( const).

Другое использование [ править ]

Кроме того, (нестатическая) функция-член может быть объявлена ​​как const. В этом случае thisуказатель внутри такой функции имеет тип, object_type const *а не просто тип object_type *. [1] Это означает, что неконстантные функции для этого объекта не могут быть вызваны изнутри такой функции, а переменные-члены не могут быть изменены. В C ++ переменная-член может быть объявлена ​​как mutable, что указывает на то, что это ограничение к ней не применяется. В некоторых случаях это может быть полезно, например, при кэшировании , подсчете ссылок и синхронизации данных.. В этих случаях логическое значение (состояние) объекта не изменяется, но объект не является физически постоянным, поскольку его побитовое представление может измениться.

Синтаксис [ править ]

В C, C ++ и D могут быть объявлены все типы данных, включая те, которые определены пользователем, constа константная корректность требует, чтобы все переменные или объекты были объявлены как таковые, если их не нужно изменять. Такое проактивное использование constделает значения «более легкими для понимания, отслеживания и рассуждений» [2] и, таким образом, повышает читаемость и понятность кода, а также упрощает работу в группах и сопровождение кода, поскольку оно передает информацию о предполагаемом использовании значения. Это может помочь компилятору, а также разработчику при рассмотрении кода. Это также может позволить оптимизирующему компилятору генерировать более эффективный код. [3]

Простые типы данных [ править ]

Для простых типов данных, не являющихся указателями, применить constквалификатор просто. Он может идти по обе стороны от некоторых типов по историческим причинам (например, const char foo = 'a';эквивалентен char const foo = 'a';). В некоторых реализациях использование constдважды (например, const char constили char const const) генерирует предупреждение, но не ошибку.

Указатели и ссылки [ править ]

Для типов указателей и ссылок значение constболее сложное - может быть либо сам указатель, либо указанное значение, либо и то, и другое const. Кроме того, синтаксис может сбивать с толку. Указатель может быть объявлен как constуказатель на доступное для записи значение, или как доступный для записи указатель на constзначение, или как constуказатель на constзначение. constУказатель не может быть переведен в точку на другой объект из одного он изначально назначен, но он может быть использован для изменения значения , что она указывает на ( так называемый pointee ). Ссылочные переменные в C ++ - это альтернативный синтаксис для constуказателей. Указатель наconstС другой стороны, объект можно переназначить, чтобы он указывал на другую ячейку памяти (которая должна быть объектом того же типа или конвертируемого типа), но его нельзя использовать для изменения памяти, на которую он указывает. constУказатель на constобъект , также могут быть объявлены и ни один не может быть использован для изменения pointee , ни быть переназначены , чтобы указать на другой объект. Следующий код иллюстрирует эти тонкости:

void  Foo (  int  *  ptr ,  int  const  *  ptrToConst ,  int  *  const  constPtr ,  int  const  *  const  constPtrToConst  ) {  * ptr  =  0 ;  // OK: изменяет данные "указателя"  ptr  =  NULL ;  // ОК: изменяет указатель * ptrToConst  =  0 ;  // Ошибка! Невозможно изменить данные "указателя"  ptrToConst  =  NULL ;  // ОК: изменяет указатель * constPtr  =  0 ;  // ОК: изменяет данные "указателя"  constPtr  =  NULL ;  // Ошибка! Невозможно изменить указатель * constPtrToConst  =  0 ;  // Ошибка! Невозможно изменить данные "указателя"  constPtrToConst  =  NULL ;  // Ошибка! Невозможно изменить указатель }

Соглашение C [ править ]

Следуя обычному соглашению C для объявлений, объявление следует за использованием, и *в указателе записывается указатель, указывающий на разыменование . Например, в объявлении int *ptrразыменованная форма *ptr- это int, а ссылочная форма ptr- это указатель на int. Таким образом constизменяет имя справа. Соглашение C ++ вместо этого заключается в том, чтобы связать *с типом, как в, int* ptr,и читать constкак, изменяя тип слева. int const * ptrToConstтаким образом, может быть прочитан как « *ptrToConstявляется int const» (значение является постоянным) или « ptrToConstявляется int const *» (указатель является указателем на постоянное целое число). Таким образом:

int  * ptr ;  // * ptr - это значение типа int int  const  * ptrToConst ;  // * ptrToConst - это константа (int: целочисленное значение) int  *  const  constPtr ;  // constPtr - константа (int *: целочисленный указатель) int  const  *  const  constPtrToConst ;  // constPtrToConst - это константа (указатель)  // как есть * constPtrToConst (значение)

Соглашение C ++ [ править ]

Следуя соглашению C ++ об анализе типа, а не значения, эмпирическое правило - читать объявление справа налево. Таким образом, все, что находится слева от звезды, может быть идентифицировано как тип указателя, а все справа от звезды - это свойства указателя. Например, в нашем примере, приведенном выше, он int const *может быть прочитан как доступный для записи указатель, который ссылается на незаписываемое целое число, и int * constможет быть прочитан как незаписываемый указатель, который ссылается на доступное для записи целое число.

Более общее правило, которое помогает понять сложные объявления и определения, работает следующим образом:

  1. найдите идентификатор, объявление которого вы хотите понять
  2. читать как можно дальше вправо (т. е. до конца объявления или до следующей закрывающей круглой скобки, в зависимости от того, что наступит раньше)
  3. вернитесь туда, где вы начали, и читайте в обратном направлении влево (то есть до начала объявления или до открытой круглой скобки, соответствующей закрывающей скобке, найденной на предыдущем шаге)
  4. когда вы дошли до начала объявления, все готово. Если нет, перейдите к шагу 2 за закрывающей круглой скобкой, которая была найдена последней.

Вот пример:

При чтении слева важно, чтобы вы читали элементы справа налево. Таким образом, an int const *становится указателем на const int, а не константным указателем на int .

В некоторых случаях C / C ++ позволяет constразмещать ключевое слово слева от типа. Вот некоторые примеры:

const  int  * ptrToConst ;  // идентично: int const * ptrToConst, const  int  * const  constPtrToConst ;  // идентично: int const * const constPtrToConst

Хотя C / C ++ допускает такие определения (которые близко соответствуют английскому языку при чтении определений слева направо), компилятор по-прежнему читает определения в соответствии с вышеупомянутой процедурой: справа налево. Но если поставить const перед тем, что должно быть постоянным, быстро появятся несоответствия между тем, что вы собираетесь написать, и тем, что компилятор решит, что вы написали. Рассмотрим указатели на указатели:

int  ** ptr ;  // указатель на указатель на целые числа int  const  ** ptr  // указатель на указатель на постоянное значение int  // (не указатель на постоянный указатель на целые числа) int  * const  * ptr  // указатель на  константу указатель на значения типа int // (не постоянный указатель на указатель на целые числа) int  ** const  ptr  // постоянный указатель на указатели на целые числа  // (ptr, идентификатор, являющийся константой , не имеет смысла) int  const  ** const  ptr  // постоянный указатель на указатели на постоянные значения типа int

В качестве последнего примечания относительно определений указателей: всегда пишите символ указателя (*) как можно правее. Прикрепить символ указателя к типу сложно, поскольку он настоятельно предполагает тип указателя, а это не так. Вот некоторые примеры:

int *  a ;  / * пишем: * /  int  * a ;  // a - указатель на int int *  a ,  b ;  // ЗАБОТА  / * запись: * /  int  * a ,  b ;  // a - указатель на int,  // но b - это просто int int *  a ,  * b ;  // УЖАСНО: и a, и b являются указателями на целые числа  / * write: * /  int  * a ,  * b ;

Часто задаваемые вопросы Бьярна Страуструпа рекомендуют объявлять только одну переменную в каждой строке при использовании соглашения C ++, чтобы избежать этой проблемы. [4]

Те же соображения применимы к определению ссылок и ссылок rvalue:

int  var  =  22 ; int  const  & refToConst  =  var ;  // ОК int  const &  ref2  =  var ,  ref3  =  var ;  // ЗАБОТА:  // ref2 - ссылка, а ref3 - нет:  // ref3 - константа int, инициализированная  // значением var int  & const  constRef  =  var ;  // ОШИБКА: ссылки все равно не могут измениться.// C ++: int &&  rref  =  int ( 5 ),  value  =  10 ;  // ЗАБОТА:  // rref - это ссылка на rvalue, но  //  значение - это просто int. / * запись: * /  int  && rref  =  int ( 5 ),  value  =  10 ;

Более сложные объявления встречаются при использовании многомерных массивов и ссылок (или указателей) на указатели. Хотя иногда спорят [ кто? ], что такие объявления сбивают с толку и подвержены ошибкам и поэтому их следует избегать или заменять структурами более высокого уровня, всегда можно использовать процедуру, описанную в верхней части этого раздела, без введения двусмысленности или путаницы.

Параметры и переменные [ править ]

constмогут быть объявлены как в параметрах функции, так и в переменных ( статических или автоматических, включая глобальные или локальные). Интерпретация варьируется в зависимости от использования. constСтатическая переменная (глобальная переменная или статическая локальная переменная) является постоянным, и может быть использовано для передачи данных , таких как математические константы, такие как double const PI = 3.14159- реально больше, или общие параметры времени компиляции. constАвтоматическая переменная (нестатический локальная переменная) означает , что одно назначение происходит, хотя другое значение может быть использовано каждый раз , когда , например int const x_squared = x * x. constПараметр передачи по ссылке означает , что упоминаемое значение не изменяется - это часть договора - в то время какconstпараметр в передаче по значению (или сам указатель в передаче по ссылке) ничего не добавляет к интерфейсу (поскольку значение было скопировано), но указывает, что внутренне функция не изменяет локальную копию параметр (это однократное присвоение). По этой причине некоторые предпочитают использовать constв параметрах только для передачи по ссылке, где он изменяет контракт, но не для передачи по значению, где он раскрывает реализацию.

C ++ [ править ]

Методы [ править ]

Чтобы воспользоваться преимуществом контрактного подхода для определяемых пользователем типов (структур и классов), которые могут иметь методы, а также данные-члены, программист может пометить методы экземпляра, как constесли бы они не изменяли элементы данных объекта. Применяя constспецификатор к методам экземпляра , таким образом , является существенным признаком для сопзЬ корректности, и не доступен во многих других объектно-ориентированных языков , таких как Java и C # или в Microsoft «s C ++ / CLI или расширений Managed для C ++ . Хотя constметоды могут вызываться как объектами, так constи не constобъектами, не constметоды могут быть вызваны только объектами, не являющимися объектами.constобъекты. constМодификатора на метод экземпляра относится к объекту , на который указывает « this» указатель, который является неявный аргумент , передаваемый всех методов экземпляра. Таким образом, наличие constметодов - это способ применить константную корректность к thisаргументу неявного указателя, как и к другим аргументам.

Этот пример иллюстрирует:

класс  C {  int  i ; public :  int  Get ()  const  // Обратите внимание на тег «const»  {  return  i ;  }  void  Set ( int  j )  // Обратите внимание на отсутствие «const»  {  i  =  j ;  } };void  Foo ( C &  nonConstC ,  C  const &  constC ) {  int  y  =  nonConstC . Получить ();  // Хорошо  int  x  =  constC . Получить ();  // Хорошо: Get () - это константа nonConstC . Набор ( 10 );  // Хорошо: nonConstC можно  изменить constC . Набор ( 10 );  // Ошибка! Set () - это неконстантный метод, а constC - это объект с квалификацией const }

В приведенном выше коде неявный thisуказатель на "" Set()имеет тип " C *const"; тогда как " this" указатель на Get()имеет тип " C const *const", указывая, что метод не может изменить свой объект с помощью thisуказателя " ".

Часто программист предоставляет как метод, так constи не constметод с одним и тем же именем (но, возможно, совершенно по-разному) в классе, чтобы учесть оба типа вызывающих. Учитывать:

класс  MyArray {  int  data [ 100 ]; общедоступные :  int  &  Get ( int  я )  {  вернуть  данные [ я ];  }  int  const  &  Get ( int  i )  const  {  вернуть  данные [ я ];  } };void  Foo (  MyArray  &  array ,  MyArray  const  &  constArray  ) {  // Получить ссылку на элемент массива  // и изменить его значение, на которое оно ссылается. массив . Получаем (  5  )  =  42 ;  // OK! (Вызов: int & MyArray :: Get (int))  constArray . Получаем (  5  )  =  42 ;  // Ошибка! (Вызов: int const & MyArray :: Get (int) const) }

const-Ness вызывающего объекта определяет , какая версия MyArray::Get()будет вызываться и , таким образом , вызывающему абоненту предоставляется ли или нет ссылки , с которыми он может манипулировать или только наблюдать за личные данные в объекте. Эти два метода технически имеют разные сигнатуры, поскольку их thisуказатели имеют разные типы, что позволяет компилятору выбрать правильный. (Возврат constссылки на объект intвместо простого возврата intпо значению может быть излишним во втором методе, но для произвольных типов можно использовать тот же метод, что и в стандартной библиотеке шаблонов .)

Лазейки для константной корректности [ править ]

Есть несколько лазеек для чистой константной корректности в C и C ++. Они существуют в первую очередь для совместимости с существующим кодом.

Первый, который применим только к C ++, - это использование const_cast, которое позволяет программисту удалять constквалификатор, делая любой объект изменяемым. Необходимость удаления квалификатора возникает при использовании существующего кода и библиотек, которые нельзя изменить, но которые не являются корректными с константой. Например, рассмотрим этот код:

// Прототип функции, которую мы не можем изменить, но которая //, как мы знаем, не изменяет переданный объект. Void  LibraryFunc ( int *  ptr ,  int  size );void  CallLibraryFunc ( int  const  *  ptr ,  int  size ) {  LibraryFunc ( ptr ,  size );  // Ошибка! Квалификатор Drops const int *  nonConstPtr  =  const_cast < int *> ( ptr );  //  Удаляем квалификатор LibraryFunc ( nonConstPtr ,  size );  // ОК }

Однако любая попытка изменить объект, который сам объявлен constс помощью константного приведения, приводит к неопределенному поведению в соответствии со стандартом ISO C ++. В приведенном выше примере, если ptrссылается на глобальную, локальную переменную или переменную-член, объявленную как const, или объект, выделенный в куче через new int const, код будет правильным, только если LibraryFuncдействительно не изменяет значение, на которое указывает ptr.

В языке C нужна лазейка, потому что существует определенная ситуация. Переменные со статической продолжительностью хранения могут быть определены с начальным значением. Однако инициализатор может использовать только константы, такие как строковые константы и другие литералы, и ему не разрешается использовать непостоянные элементы, такие как имена переменных, независимо от того, объявлены ли элементы инициализатора constили нет, или объявлена ​​ли статическая переменная длительности constили нет. Существует непереносимый способ инициализировать constпеременную со статической продолжительностью хранения. Тщательно сконструировав приведение типов в левой части более позднего присваивания, constпеременная может быть записана, эффективно удаляя constатрибут и `` инициализируя '' его непостоянными элементами, такими как другиеconstпеременные и тому подобное. Запись в constпеременную таким образом может работать как задумано, но вызывает неопределенное поведение и серьезно противоречит константной корректности:

size_t  const  bufferSize  =  8 * 1024 ; size_t  const  userTextBufferSize ;  // начальное значение зависит от const bufferSize, здесь не может быть инициализировано...int  setupUserTextBox ( textBox_t  * defaultTextBoxType ,  rect_t  * defaultTextBoxLocation ) {  * ( size_t * ) & userTextBufferSize  =  bufferSize  -  sizeof ( struct  textBoxControls );  // предупреждение: может работать, но не гарантируется C  ... }

Еще одна лазейка [5] применима как к C, так и к C ++. В частности, языки диктуют, что указатели на члены и ссылки должны быть «мелкими» по отношению к constих владельцам, то есть содержащий объект, который constимеет все constчлены, за исключением тех, кто указывает на члены (и рефери), по-прежнему изменяемыми. Чтобы проиллюстрировать это, рассмотрим этот код C ++:

struct  S {  int  val ;  int  * ptr ; };void  Foo ( S  const  &  s ) {  int  я  =  42 ;  с . val  =  i ;  // Ошибка: s равно const, поэтому val - const int  s . ptr  =  & я ;  // Ошибка: s является константой, поэтому ptr - константный указатель на int  * s . ptr  =  i ;  // ОК: данные, на которые указывает ptr, всегда изменяемы,  // даже если это иногда нежелательно }

Хотя объект, sпереданный в, Foo()является константой, что делает все его члены постоянными, объект, доступный через указатель, по- s.ptrпрежнему может быть constизменен , хотя это может быть нежелательно с точки зрения правильности, поскольку он sможет принадлежать только объекту. По этой причине Мейерс утверждает, что значение по умолчанию для указателей и ссылок на элементы должно быть «глубоким» const, что может быть переопределено mutableквалификатором, если указатель не принадлежит контейнеру, но эта стратегия создаст проблемы совместимости с существующим кодом. Таким образом, по историческим причинам [ необходима цитата ] , эта лазейка остается открытой в C и C ++.

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

Наконец, несколько функций в стандартной библиотеке C нарушают константную корректность, поскольку они принимают constуказатель на символьную строку и возвращают не- constуказатель на часть той же строки. strtolи strchrвходят в число этих функций. Некоторые реализации стандартной библиотеки C ++, такие как Microsoft [6], пытаются закрыть эту лазейку, предоставляя две перегруженные версии некоторых функций: " const" версию и "не const" версию.

Проблемы [ править ]

Использование системы типов для выражения постоянства приводит к различным сложностям и проблемам и, соответственно, подвергалось критике и не принималось за пределами узкого семейства C, включая C, C ++ и D. Java и C #, на которые сильно влияют C и C ++, оба явно отклонили constквалификаторы типа -style, вместо этого выражая постоянство ключевыми словами, которые применяются к идентификатору ( finalв Java constи readonlyC #). Даже в пределах C и C ++ использование constзначительно различается: одни проекты и организации используют его последовательно, а другие избегают.

strchrпроблема [ править ]

constТипа классификатор вызывает трудности , когда логика функции является агностиком ли его вход является постоянным или нет, но возвращает значение , которое должно быть того же типа , квалифицированным в качестве входных данных. Другими словами, для этих функций, если ввод является константой (квалифицированным константой), возвращаемое значение также должно быть, но если ввод является переменным ( constнеквалифицированным), возвращаемое значение также должно быть. Поскольку сигнатура типа этих функций различается, для этого требуются две функции (или, возможно, больше, в случае нескольких входов) с одинаковой логикой - форма общего программирования .

Эта проблема возникает даже для простых функций в стандартной библиотеке C, в частности strchr; Это наблюдение Ричи приписывает Тому Пламу в середине 1980-х годов. [7]strchr функция находит символ в строке; формально он возвращает указатель на первое вхождение символа cв строке s, а в классическом C (K&R C) его прототипом является:

char  * strchr ( char  * s ,  int  c );

strchrФункция не изменяет входной строки, но возвращаемое значение часто используется вызывающим для изменения строки, такие как:

если  ( p  =  strchr ( q ,  '/' ))  * p  =  '' ;

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

В C ++ это делается с помощью перегрузки функций , обычно реализуемой через шаблон , в результате чего constсоздаются две функции, так что возвращаемое значение имеет тот же -квалифицированный тип, что и вход: [c]

char *  strchr ( char *  s ,  int  c ); char  const *  strchr ( char  const *  s ,  int  c );

Их, в свою очередь, можно определить с помощью шаблона:

template  < T > T *  strchr ( T *  s ,  int  c )  {  ...  }

В D это обрабатывается с помощью inoutключевого слова, которое действует как подстановочный знак для const, неизменяемого или неквалифицированного (переменной), что дает: [8] [d]

inout ( char ) *  strchr ( inout ( char ) *  s ,  int  c );

Однако в C ни то, ни другое невозможно, поскольку C не имеет перегрузки функций, и вместо этого это обрабатывается с помощью единственной функции, где вход постоянен, но выход доступен для записи:

char  * strchr ( char  const  * s ,  int  c );

Это позволяет использовать идиоматический код C, но удаляет квалификатор const, если ввод действительно квалифицирован как const, что нарушает безопасность типов. Это решение было предложено Ричи и впоследствии принято. Это отличие - один из недостатков совместимости C и C ++ .

D [ править ]

В версии 2 языка программирования D существуют два ключевых слова, относящиеся к const. [9] В immutableКлючевое слово обозначает данные , которые не могут быть модифицированы путем какой - либо ссылки. constКлючевое слово обозначает не-изменчивый вид изменяемых данных. В отличие от C ++ const, D constи immutableявляются «глубокие» или транзитивно , и все , что достижимы через constили immutableобъект constили immutableсоответственно.

Пример константы и неизменяемости в D

int []  foo  =  новый  int [ 5 ];  // foo изменяемый. const  int []  bar  =  foo ;  // bar - это константное представление изменяемых данных. неизменяемый  int []  baz  =  foo ;  // Ошибка: все представления неизменяемых данных должны быть неизменяемыми.неизменяемый  int []  nums  =  новый  неизменяемый ( int ) [ 5 ];  // Невозможно создать изменяемую ссылку на nums. Const  INT []  constNums  =  НУМС ;  // Работает. immutable неявно преобразуется в const. INT []  mutableNums  =  НУМС ;  // Ошибка: невозможно создать изменяемое представление неизменяемых данных.

Пример транзитивной или глубокой константы в D

class  Foo  {  Foo  next ;  int  num ; }неизменяемый  Foo  foo  =  новый  неизменяемый ( Foo ); фу . следующий . число  =  5 ;  // Не компилируется. foo.next имеет тип неизменяемый (Foo).  // foo.next.num имеет тип неизменяемый (int).

История [ править ]

constбыл представлен Бьярном Страуструпом в языке C с классами , предшественником C ++ , в 1981 году и первоначально назывался readonly. [10] [11] Что касается мотивации, Страуструп пишет: [11]

«Он выполнял две функции: как способ определения символической константы, которая подчиняется правилам области и типа (то есть без использования макроса), и как способ считать объект в памяти неизменным».

Первое использование в качестве альтернативы макросам с заданной областью и типом аналогично выполнялось для макросов, подобных функциям, через inlineключевое слово. Постоянные указатели и * constобозначения были предложены Деннисом Ричи и поэтому приняты. [11]

constзатем был принято в C в рамках стандартизации, и появляется в C89 (и последующие версии) , наряду с другим типом классификатором, volatile. [12] На noaliasзаседании комитета X3J11 в декабре 1987 г. был предложен дополнительный квалификатор , но он был отклонен; в конечном итоге его цель была достигнута с помощью restrictключевого слова в C99 . Ричи не очень поддерживал эти дополнения, утверждая, что они не «несут их вес», но в конечном итоге не выступал за их исключение из стандарта. [13]

Впоследствии D унаследован constот C ++, где он известен как конструктор типа (не квалификатор типа ), и добавил еще два конструктора типа immutableи inoutдля обработки связанных вариантов использования. [e]

Другие языки [ править ]

Другие языки не следуют C / C ++ в части постоянства типа, хотя они часто имеют внешне похожие конструкции и могут использовать constключевое слово. Обычно это используется только для констант (постоянных объектов).

В C # есть constключевое слово, но с радикально другой и более простой семантикой: оно означает константу времени компиляции и не является частью типа.

В Nim есть constключевое слово, аналогичное ключевому слову C #: он также объявляет константу времени компиляции, а не является частью типа. Однако в Nim константу можно объявить из любого выражения, которое можно вычислить во время компиляции. [14] В C # только встроенные типы C # могут быть объявлены как const; пользовательские типы, включая классы, структуры и массивы, не могут быть const. [15]

В Java нет const- вместо этого есть final, что может быть применено к объявлениям локальных «переменных» и применяется к идентификатору, а не к типу. Он имеет другое объектно-ориентированное использование для членов объекта, что является источником имени.

Спецификация языка Java рассматривает constкак зарезервированное ключевое слово, т. Е. Такое , которое нельзя использовать в качестве идентификатора переменной, но не присваивает ему семантику: это зарезервированное слово (его нельзя использовать в идентификаторах), но не ключевое слово (оно не имеет специального смысл). Считается, что зарезервировано ключевое слово, чтобы позволить расширение языка Java включить constметоды в стиле C ++ и указатель на constтип. [ необходима цитата ] Билет запроса на расширение для реализации constкорректности существует в Java Community Process , но был закрыт в 2005 году на том основании, что его невозможно было реализовать обратно совместимым способом. [16]

Современная Ada 83 независимо друг от друга имела понятие константного объекта и constantключевого слова [17] [f] с неявно постоянными входными параметрами и параметрами цикла. Здесь constant- свойство объекта, а не его типа.

В JavaScript есть constобъявление, определяющее переменную с блочной областью видимости, которую нельзя переназначить или повторно объявить. Он определяет доступную только для чтения ссылку на переменную, которую нельзя переопределить, но в некоторых ситуациях значение самой переменной потенциально может измениться, например, если переменная ссылается на объект и его свойство изменяется. [18]

См. Также [ править ]

  • Однократное присвоение
  • ограничивать
  • Наложение указателя

Заметки [ править ]

  1. ^ В D термин конструктор типа используется вместо квалификатора типа по аналогии с конструкторами в объектно-ориентированном программировании .
  2. ^ Формально, когдаconstявляется частью самого внешнего производного типа в объявлении; указатели усложняют обсуждение.
  3. ^ Обратите внимание, что соглашения о синтаксисе объявления указателя различаются между C и C ++: Cchar *sявляется стандартным, а C ++char* s- стандартным.
  4. ^ Идиоматический код D будет использовать здесь массив вместо указателя. [8]
  5. ^ D также ввелsharedконструктор типа, но это связано с использованием случаевvolatile, неconst.
  6. ^ Стандарт Ады называет это « зарезервированным словом »; см. эту статью для использования.

Ссылки [ править ]

  1. ^ " thisУказатель" . Проект стандарта C ++ . Проверено 30 марта 2020 . Тип thisв функции-члене, тип которой имеет cv-qualifier-seq cv, а класс X- «указатель на cv X ».
  2. ^ Херб Саттер и Андрей Александреску (2005). Стандарты кодирования C ++ . п. 30. Бостон: Эддисон Уэсли. ISBN 0-321-11358-6 
  3. ^ "Почему аргумент kfree () является константой?" . lkml.org. 2013-01-12.
  4. ^ "Страуструп: FAQ по стилю и технике C ++" .
  5. ^ Скотт Мейерс (2005). Эффективный C ++, третье издание . С. 21-23. Бостон: Эддисон Уэсли. ISBN 978-0-321-33487-9 
  6. ^ "strchr, wcschr, _mbschr (CRT)" . Msdn.microsoft.com . Проверено 23 ноября 2017 .
  7. ^ «Деннис Ричи: Почему мне не нравятся квалификаторы типа X3J11» .
  8. ^ a b Язык программирования D, Андрей Александреску , 8.8: распространение квалификатора от параметра к результату
  9. ^ "const (FAQ) - Язык программирования D" . Digitalmars.com . Проверено 18 августа 2013 .
  10. ^ Бьярне Страуструп , "Расширения языка C Type Concept.", Bell Labs внутренний технический меморандум, 5 января 1981.
  11. ^ a b c Соперничество братьев и сестер: C и C ++ , Бьярн Страуструп , 2002, стр. 5
  12. ^ Dennis M. Ritchie , « Развитие С языка В архиве 15 июля 2012 г., Archive.today », 2003: «X3J11 также представил множество мелких дополнений и корректировок, например, классификаторы типа константные и изменчивы , и немного другие правила продвижения типа ".
  13. ^ "Позвольте мне начать с того, что я не уверен, что даже квалификаторы до декабря ('const' и 'volatile') имеют свой вес; я подозреваю, что то, что они добавляют к стоимости изучения и использования языка, не вознаграждаются большей выразительностью. «Volatile», в частности, является украшением для эзотерических приложений и гораздо лучше выражается другими средствами. Его главное достоинство в том, что почти каждый может о нем забыть. «Const» одновременно более полезен и навязчив; вы не можете не узнать об этом из-за его присутствия в интерфейсе библиотеки. Тем не менее, я не выступаю за исключение квалификаторов, хотя бы потому, что уже слишком поздно ».
  14. ^ Руководство Nim: раздел Const
  15. ^ const (Справочник по C #)
  16. ^ "Идентификатор ошибки: JDK-4211070 Java должна поддерживать константные параметры (например, C ++) для поддержки кода [ sic ]" . Bugs.sun.com . Проверено 4 ноября 2014 .
  17. ^ 1815A [ мертвая ссылка ] , 3.2.1. Объявления объектов, заархивированные 20 октября 2014 года на Wayback Machine :
    «Объявленный объект является константой, если в объявлении объекта присутствует зарезервированная словесная константа; в этом случае объявление должно включать явную инициализацию. Значение константы не может быть изменено после инициализации Формальные параметры режима in подпрограмм и записей, а также общие формальные параметры режима in также являются константами; параметр цикла - это константа в соответствующем цикле; подкомпонент или фрагмент константы является константой ».
  18. ^ "const" . MDN . Проверено 31 октября 2017 года .

Внешние ссылки [ править ]

  • «Уста-Корректность» по Herb Sutter
  • "Постоянная оптимизация?" Херб Саттер
  • C ++ FAQ Lite: правильность констант Маршалла Клайна
  • Раздел « Подстановка значений » из бесплатной электронной книги « Мышление на C ++ » Брюса Экеля.
  • «Здесь A Const, там Const» от Уолтера Брайта
  • "Const and Invariant" из спецификации языка программирования D, версия 2 (экспериментальная)