Const (программирование)
В языках программирования C, C++, C# и D const является квалификатором типа:[lower-alpha 1] ключевое слово применяется к типу данных, показывая, что данные константны (неизменяемы). Это может быть использовано при объявлении (декларировании) констант. Отличительная особенность const
в C-подобных языках программирования проявляется при его комбинировании с типами данных, что дает сложное поведение в сочетании с указателями, ссылками, составными типами данных и при проверке типов.
Введение
При объявлении[lower-alpha 2] объектов с использованием const их значения константны (неизменяемы), в отличие от перeменных. Этот основной сценарий использования — объявление констант — имеет параллели во многих языках.
Однако в отличие от других языков в семье языков C const
является частью типа, а не объекта. Например, в C const int x = 1;
объявляет объект x
типа const int
, где const
— часть типа. Это может читаться «(const int) x» — в то время как в Ada X : constant INTEGER := 1_
объявляет константу (вид объекта) X
типа INTEGER
, т. е. constant
является частью объекта, а не типа.
Появляются два тонких момента. Во-первых, const
может быть частью более сложных типов. Например, int const * const x;
(выражение читается от переменной x в противоположную сторону) объявляет константный указатель на константное целое число, в то время как int const * x;
объявляет переменную-указатель на константное целое, а int * const x;
объявляет константный указатель на переменное целое. Во-вторых, поскольку const
является частью типа, это используется при проверке типов. Например, следующий код некорректен:
void f(int& x);
//...
const int i;
f(i);
потому что аргумент, передаваемый в f
, должен быть ссылочной переменной на целое, а i
— константное целое. Требование такого соответствия — форма обеспечения правильности программы, также известное как const
-овая правильность (англ. const
-correctness). Это дает возможность контрактного проектирования, где у функций в качестве части сигнатуры типа указывается, будут ли они изменять свои аргументы или нет, и изменяемы (неконстантны) ли их возвращаемые значения. Такая проверка типов интересна прежде всего для указателей и ссылок (т. е. когда параметры передаются по ссылке) — а не для основных типов, таких как целые, — а также для составных типов данных или шаблонизированных типов, таких как контейнеры. Вследствие неявного преобразования типов при выполнении программы const
может быть опущен.
Следствия
При сохранении в памяти компьютера, константность не накладывает на значение ограничения на запись. const
— это скорее конструкция времени компиляции, которую программист потенциально может использовать, однако это необязательно. Стоит обратить внимание, что в случае предопределенных строковых литералов (таких как const char *) константное значение в языке Си (и Си++) обычно неперезаписываемо, так как оно может храниться в сегменте памяти с запретом записи.
Другие варианты использования
В дополнение к этому как const
может быть объявлена функция-член (нестатическая). В этом случае указатель this
внутри такой функции будет иметь тип object_type const * const
вместо object_type * const
. Это означает, что неконстантные по отношению к этому объекту функции изнутри такой функции вызваны быть не могут, также не могут быть модифицированы поля класса. В C++ поле класса может быть объявлено как mutable
(изменяемое), что означает, что это ограничение к нему не относится. В некоторых случаях это может быть полезно, например, при кешировании, подсчёте ссылок и синхронизации данных. В этих случаях логический смысл (состояние) объекта неизменяем, но объект физически неконстантен, так как его поразрядное представление может измениться.
Синтаксис
В C, C++, и D все типы данных, включая те, которые определены пользователем, могут быть объявлены const
, и «const
-овая правильность» предполагает, что все переменные или объекты должны быть объявлены таковыми, если их не нужно модифицировать. Такое предусмотрительное использование const
делает значения переменных "простыми для понимания, отслеживания, и обдумывания"[1], таким образом, читаемость и понятность увеличиваются и делают работу в командах и поддержку кода проще, потому что это предоставляет информацию о надлежащем использовании их значений. Это может помочь компилятору так же, как и разработчику при размышлениях над кодом. Это также может позволить оптимизирующему компилятору генерировать более эффективный код[2].
Простые типы данных
Для простых типов данных (неуказателей) применение квалификатора const
очевидно. Его можно указывать с любой стороны типа по историческим причинам (const char foo = 'a';
эквивалентно char const foo = 'a';
). В некоторых реализациях использование const
с двух сторон типа (например, const char const
) генерирует предупреждение, но не ошибку.
Указатели и ссылки
Для указателей и ссылок результирующий эффект const
более запутан: и сам указатель, и значение, на которое он указывает, или оба могут быть объявлены как const
. Более того, синтаксис также может сбивать с толку.
Указатель может быть объявлен const
-указателем на перезаписываемое значение (type * const x;
), или перезаписываемым указателем на const
-значение (type const * x; //или: const type * x;
), или const
-указателем на const
-значение (type const * const x;
). const
-указателю нельзя переприсвоить ссылку на другой объект с первоначального, но он (указатель) может быть использован для изменения значения, на которое он указывает (такое значение называется «значение по указателю» (англ. pointee)). Таким образом, синтаксис ссылочных переменных — альтернативный синтакс const
-указателей. С другой стороны, указателю на const
-объект можно переприсвоить ссылку для указания на другую область памяти (которая должна содержать объект того же или приводимого типа), но его нельзя будет использовать, чтобы менять значения в памяти, на которые он указывает. Также может быть определен const
-указатель на const
-объект, который нельзя использовать, чтобы изменять значение по нему, и которому нельзя переприсвоить ссылку на другой объект.
Эти тонкости иллюстрирует следующий код:
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
В соответствие с обычными соглашениями языка Си об объявлениях последние указываются за предполагаемым использованием, а звёздочка у указателя к нему ставится вплотную, указывая на разыменование. Например, в объявлении int *ptr
разыменованная форма *ptr
является целым (int
), а ссылочная форма ptr
— указателем на целое. Таким образом, const
модифицирует имя переменной справа от себя.
Соглашение в языке Си++ – наоборот, связывать *
с типом (т. е. int* ptr
) и читать, что const
модифицирует тип слева от себя. Поэтому int const * ptrToConst
можно прочитать или как «*ptrToConst
– это int const
» (значение по указателю неизменяемо), или как «ptrToConst
– это int const *
» (указатель на неизменяемое целое значение).
Таким образом:
int *ptr; //"*ptr" -- целое значение.
int const *ptrToConst; //"*ptrToConst" -- константа ("int" -- целая).
int * const constPtr; //"constPtr" -- константа ("int *" -- указатель на целое).
int const * const constPtrToConst; //"constPtrToConst" -- константа (указатель),
//как и "*constPtrToConst" (значение).
Примечания
- In D the term type constructor is used instead of type qualifier, by analogy with constructors in object-oriented programming.
- Formally when the
const
is part of the outermost derived type in a declaration; pointers complicate discussion.
- Herb Sutter and Andrei Alexandrescu (2005).
- Why is the kfree() argument const? (недоступная ссылка). lkml.org (12 января 2013). Дата обращения: 9 января 2016. Архивировано 4 марта 2016 года.