Меню
Главная
Случайная статья
Настройки
|
В языках программирования C, C++, C# и D const является квалификатором типа:[a] ключевое слово применяется к типу данных, показывая, что данные константны (неизменяемы). Это может быть использовано при объявлении (декларировании) констант. Отличительная особенность const в C-подобных языках программирования проявляется при его комбинировании с типами данных, что дает сложное поведение в сочетании с указателями, ссылками, составными типами данных и при проверке типов.
Содержание
Введение
При объявлении[b] объектов с использованием 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; //Ошибка! Нельзя менять указатель.
}
В соответствие с обычными соглашениями языка Си об объявлениях последние указываются за предполагаемым использованием, а звёздочка у указателя к нему ставится вплотную, указывая на разыменование. Например, в объявлении 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 года.
|
|