Квалификатор типа — это аналог конструкторов в объектно-ориентированном программировании .
По состоянию на 2014 год [обновлять]и C11 в стандарте C есть четыре квалификатора типа: const
( C89 ), volatile
( C89 ), restrict
( C99 ) и _Atomic
( C11 ) – последний имеет личное имя, чтобы избежать конфликта с именами, определенными пользователем. [1] Первые два из них, const
и volatile
, также присутствуют в C++ и являются единственными квалификаторами типа в C++. Таким образом, в C++ термин « cv -qualified type» (для const и v olatile) часто используется для «квалифицированного типа», в то время как термины « c -qualified type» и « v -qualified type» используются, когда важен только один из квалификаторов.
Из них, const
безусловно, является самым известным и наиболее используемым, появляющимся в стандартных библиотеках C и C++ и встречающимся в любом значительном использовании этих языков, которое должно удовлетворять const-correctness . Другие квалификаторы используются для низкоуровневого программирования, и хотя широко используются там, редко используются типичными программистами. Однако некоторое время volatile
использовался некоторыми программистами C++ для синхронизации во время потоковой обработки, хотя это не поощрялось и теперь сломано в большинстве компиляторов.
В языке D конструкторами типов являются const
, immutable
, shared
, и inout
. immutable
— более сильный вариант const
, указывающий на данные, которые никогда не могут изменить свое значение, в то время как const
указывает на данные, которые не могут быть изменены посредством этой ссылки: это постоянное представление для потенциально изменяемы данных. shared
используется для общих данных в многопоточности (как volatile
вкратце использовалось в C++). inout
— это подстановочный знак, используемый для того, чтобы функции, которые не изменяют данные (и, таким образом, имеют дело только с неквалифицированным типом данных), могли возвращать тот же квалифицированный тип, что и входные данные. const
и immutable
также могут использоваться в качестве спецификаторов класса хранения.
В C и C++ тип задается в объявлении функции или переменной путем указания одного или нескольких спецификаторов типа и, по желанию, квалификаторов типа. Например, целочисленная переменная может быть объявлена как:
int x ;
где int
— спецификатор типа. Целочисленная переменная без знака может быть объявлена как:
беззнаковое целое число x ;
где unsigned
и int
являются спецификаторами типа. Константная беззнаковая целая переменная может быть объявлена как:
константа беззнаковое целое число x ;
где const
— квалификатор типа, квалифицированный тип которого x
— const unsigned int
, а неквалифицированный тип — unsigned int
.
Объявления переменных также имеют необязательный спецификатор класса хранения . По сути, это отдельная тема, отличная от типа, хотя const
объявление переменной также подразумевает последствия для класса хранения, а именно, что она может храниться в памяти, доступной только для чтения.
Другой квалификатор в C и C++, volatile
, указывает на то, что объект может быть изменен чем-то внешним по отношению к программе в любое время и поэтому должен быть повторно считан из памяти каждый раз при обращении к нему.
Квалификатор чаще всего встречается в коде, который напрямую манипулирует оборудованием (например, во встроенных системах и драйверах устройств ) и в многопоточных приложениях (хотя часто используется неправильно в этом контексте; см. внешние ссылки на volatile variable ). Его можно использовать точно так же, как const
в объявлениях переменных, указателей, ссылок и функций-членов, и фактически volatile
иногда используется для реализации похожей стратегии проектирования по контракту, которую Андрей Александреску называет volatile
-correctness, [2], хотя это встречается гораздо реже, чем const
-correctness. volatile
Квалификатор также может быть удален с помощью const_cast
, и его можно объединить с const
квалификатором, как в этом примере:
// Устанавливаем ссылку на аппаратный регистр, доступный только для чтения, который // отображается в жестко закодированном месте памяти. const volatile int & hardwareRegister = * reinterpret_cast < int *> ( 0x8000 ); int currentValue = hardwareRegister ; // Прочитать ячейку памяти int newValue = hardwareRegister ; // Прочитать ее снова hardwareRegister = 5 ; // Ошибка, невозможно записать в константное местоположение
Поскольку hardwareRegister
is volatile
, нет гарантии, что он будет содержать то же самое значение при двух последовательных чтениях, даже если программист не может его изменить. Семантика здесь указывает на то, что значение регистра доступно только для чтения, но не обязательно неизменно.
Понятие квалификатора типа было введено вместе с примером readonly
(позже переименованным const
) Бьярном Страуструпом во внутреннем техническом меморандуме Bell Labs 1981 года [3] и реализовано в языке C with Classes , предшественнике C++ . [4] Что касается мотивации, Страуструп пишет: [4]
const
Затем был принят в C как часть стандартизации и появился в C89 (и последующих версиях) вместе с другим квалификатором типа, volatile
, который был изобретен комитетом по стандарту ANSI C (X3J11). [5] volatile
появился к 1985 году; [6] и одним из первых использовался при компиляции ядра UNIX для MIPS , чтобы обеспечить оптимизированную компиляцию путем предотвращения применения обычных оптимизаций к изменчивым переменным. [7] Еще один квалификатор, noalias
, был предложен на заседании комитета X3J11 в декабре 1987 года, но был отклонен; его цель в конечном итоге была достигнута квалификатором restrict
в C99. Мотивация noalias
была дополнительной к volatile
, а именно, что он указывал на то, что даже обычно небезопасные оптимизации могут быть выполнены. Ритчи не очень поддерживал квалификаторы типа, утверждая, что они не «несут своего веса», но в конечном итоге не выступал за их удаление из стандарта; [8] он все же выступил против noalias
, и он был исключен из проекта.
В Java нет квалификаторов типов, и они явно пропущены const
: предложение 1999 года добавить их было отклонено, в частности, потому, что добавление их после факта и последующее изменение стандартной библиотеки для их последовательного использования нарушило бы совместимость. [9] Однако изначально Java оставляла открытой возможность реализации const
, что заметно по тому, что const
это зарезервированное слово , хотя оно фактически не используется как ключевое слово . Вместо этого в Java есть ключевое слово object-oriented final
, которое используется для квалификации атрибутов (и, следовательно, также для локальных переменных) как констант, но не для квалификации типов.
Другие языки используют другой подход, считая константность свойством идентификатора ( или привязки имени ), а не типа. Таким образом, такие языки имеют константные идентификаторы (соответствующие "переменным", которые не изменяются) с одним присваиванием, но не имеют понятия константной корректности: поскольку константность не является частью типа, нет возможности несоответствия типов. Примерами служат Ada 83 с константными объектами и constant
ключевым словом, [10] [a] и Java с final
ключевым словом.