В C , C ++ , D , JavaScript и Джулии языков программирования , сопзЬ является тип Классификатор : [а] ключевое слово применяется к типу данных , который указывает , что данные только для чтения. Хотя это можно использовать для объявления констант , const
в семействе языков C отличается от аналогичных конструкций в других языках тем, что он является частью типа и, следовательно, имеет сложное поведение в сочетании с указателями , ссылками, составными типами данных и проверкой типов.
Введение [ править ]
При применении в качестве объекта декларации , [Ь] это указывает на то, что объект является постоянной : его значение не может быть изменено, в отличие от переменной . Это основное использование - объявление констант - имеет параллели во многих других языках.
Однако, в отличие от других языков, в семействе языков C const
является частью типа , а не частью объекта . Например, в C, объявляет объект из типа - является частью типа, как если бы это были разобраны «(Int Const) х» - а в Ada , объявляет константу (вид объекта) из типа: это часть объекта , но не часть типа .int const x = 1;
x
int const
const
X : constant INTEGER := 1_
X
INTEGER
constant
Это дает два тонких результата. Во-первых, 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 ; // ОК: изменяет данные "указателя" 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
может быть прочитан как незаписываемый указатель, который ссылается на доступное для записи целое число.
Более общее правило, которое помогает понять сложные объявления и определения, работает следующим образом:
- найдите идентификатор, объявление которого вы хотите понять
- читать как можно дальше вправо (т. е. до конца объявления или до следующей закрывающей круглой скобки, в зависимости от того, что наступит раньше)
- вернитесь туда, где вы начали, и читайте в обратном направлении влево (то есть до начала объявления или до открытой круглой скобки, соответствующей закрывающей скобке, найденной на предыдущем шаге)
- когда вы дошли до начала объявления, все готово. Если нет, перейдите к шагу 2 за закрывающей круглой скобкой, которая была найдена последней.
Вот пример:
Часть выражения | double ( ** const ( * fun ( int )) ( double )) [ 10 ] | Значение (чтение вниз) |
---|---|---|
Идентификатор | веселье | fun это ... |
Читать вправо | ( INT )) | функция ожидает int ... |
Найдите совпадение ( | ( * | возвращая указатель на ... |
Продолжайте двигаться правильно | ( двойной )) | функция, ожидающая двойного ... |
Найдите совпадение ( | ( ** const | возвращает постоянный указатель на указатель на ... |
Продолжайте двигаться правильно | [ 10 ] | блоки по 10 ... |
Читать слева | двойной | удваивается. |
При чтении слева важно, чтобы вы читали элементы справа налево. Таким образом, 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 ; // ОК! (Вызов: 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
" версию.
Проблемы [ править ]
Этот раздел нуждается в расширении . Вы можете помочь, добавив к нему . ( Ноябрь 2014 г. ) |
Использование системы типов для выражения постоянства приводит к различным сложностям и проблемам и, соответственно, подвергалось критике и не принималось за пределами узкого семейства C, включая C, C ++ и D. Java и C #, на которые сильно влияют C и C ++, оба явно отклонили const
квалификаторы типа -style, вместо этого выражая постоянство ключевыми словами, которые применяются к идентификатору ( final
в Java const
и readonly
C #). Даже в пределах 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]
См. Также [ править ]
- Однократное присвоение
- ограничивать
- Наложение указателя
Заметки [ править ]
- ^ В D термин конструктор типа используется вместо квалификатора типа по аналогии с конструкторами в объектно-ориентированном программировании .
- ^ Формально, когда
const
является частью самого внешнего производного типа в объявлении; указатели усложняют обсуждение. - ^ Обратите внимание, что соглашения о синтаксисе объявления указателя различаются между C и C ++: в C
char *s
является стандартным, а в C ++char* s
- стандартным. - ^ Идиоматический код D будет использовать здесь массив вместо указателя. [8]
- ^ D также ввел
shared
конструктор типа, но это связано с использованием случаевvolatile
, неconst
. - ^ Стандарт Ады называет это « зарезервированным словом »; см. эту статью для использования.
Ссылки [ править ]
- ^ " thisУказатель" . Проект стандарта C ++ . Проверено 30 марта 2020 .
Тип
this
в функции-члене, тип которой имеет cv-qualifier-seq cv, а классX
- «указатель на cvX
». - ^ Херб Саттер и Андрей Александреску (2005). Стандарты кодирования C ++ . п. 30. Бостон: Эддисон Уэсли. ISBN 0-321-11358-6
- ^ "Почему аргумент kfree () является константой?" . lkml.org. 2013-01-12.
- ^ "Страуструп: FAQ по стилю и технике C ++" .
- ^ Скотт Мейерс (2005). Эффективный C ++, третье издание . С. 21-23. Бостон: Эддисон Уэсли. ISBN 978-0-321-33487-9
- ^ "strchr, wcschr, _mbschr (CRT)" . Msdn.microsoft.com . Проверено 23 ноября 2017 . CS1 maint: обескураженный параметр ( ссылка )
- ^ «Деннис Ричи: Почему мне не нравятся квалификаторы типа X3J11» .
- ^ a b Язык программирования D, Андрей Александреску , 8.8: распространение квалификатора от параметра к результату
- ^ "const (FAQ) - Язык программирования D" . Digitalmars.com . Проверено 18 августа 2013 . CS1 maint: обескураженный параметр ( ссылка )
- ^ Бьярне Страуструп , "Расширения языка C Type Concept.", Bell Labs внутренний технический меморандум, 5 января 1981.
- ^ a b c Соперничество братьев и сестер: C и C ++ , Бьярн Страуструп , 2002, стр. 5
- ^ Dennis M. Ritchie , « Развитие С языка В архиве 15 июля 2012 г., archive.today », 2003: «X3J11 также представил множество мелких дополнений и корректировок, например, классификаторы типа константные и изменчивы , и немного другие правила продвижения типа ".
- ^ "Позвольте мне начать с того, что я не уверен, что даже квалификаторы до декабря ('const' и 'volatile') имеют свой вес; я подозреваю, что то, что они добавляют к стоимости изучения и использования языка, не вознаграждаются большей выразительностью. «Volatile», в частности, является украшением для эзотерических приложений и гораздо лучше выражается другими средствами. Его главное достоинство в том, что почти каждый может о нем забыть. «Const» одновременно более полезен и навязчив; вы не можете не узнать об этом из-за его присутствия в интерфейсе библиотеки. Тем не менее, я не выступаю за исключение квалификаторов, хотя бы потому, что уже слишком поздно ».
- ^ Руководство Nim: раздел Const
- ^ const (Справочник по C #)
- ^ "Идентификатор ошибки: JDK-4211070 Java должна поддерживать константные параметры (например, C ++) для поддержки кода [ sic ]" . Bugs.sun.com . Проверено 4 ноября 2014 . CS1 maint: обескураженный параметр ( ссылка )
- ^ 1815A [ мертвая ссылка ] , 3.2.1. Объявления объектов, заархивированные 20 октября 2014 года на Wayback Machine :
«Объявленный объект является константой, если в объявлении объекта присутствует зарезервированная словесная константа; в этом случае объявление должно включать явную инициализацию. Значение константы не может быть изменено после инициализации. Формальные параметры режима in подпрограмм и записей, а также общие формальные параметры режима in также являются константами; параметр цикла - это константа в соответствующем цикле; подкомпонент или фрагмент константы является константой ». - ^ "const" . MDN . Проверено 31 октября 2017 года . CS1 maint: обескураженный параметр ( ссылка )
Внешние ссылки [ править ]
- «Уста-Корректность» по Herb Sutter
- "Постоянная оптимизация?" Херб Саттер
- C ++ FAQ Lite: правильность констант Маршалла Клайна
- Раздел « Подстановка значений » из бесплатной электронной книги « Мышление на C ++ » Брюса Экеля.
- «Здесь A Const, там Const» от Уолтера Брайта
- "Const and Invariant" из спецификации языка программирования D, версия 2 (экспериментальная)