Квалификатор const в С++
Квалификатор типов const (от анг. type qualifier) – зарезервированное ключевое слово в языке программирования С++, которое не даёт модифицировать значения переменных. Отсюда и возникло в программировании такое понятие, как константы или константные переменные, то есть неизменные. Необходимо запомнить, что при объявлении константы, её нужно инициализировать.
//Синтаксис объявления константы const /*тип данных*/ /*имя переменной*/ = /*инициализатор ( значение) */; // объявление константной переменной
Объявление константной переменной от обычной переменной отличается ключевым словом const , которое ставится перед типом данных объявляемой переменной. Рассмотрим фрагмент кода объявления константной переменной.
// объявление и инициализация константной переменной типа int с именем value const int value = 8;
Была объявлена константа целого типа с именем value и инициализирована значением 8. Если убрать инициализатор и откомпилировать программу, то компилятор выдаст ошибку похожую на эту (см. Рисунок 1).
Рисунок 1- Квалификатор const в С++
В зависимости от компилятора сообщение об ошибке может быть другим, но суть ошибки не поменяется. Данное сообщение было сгенерировано компилятором MVS2010. Компилятор сообщает, что константную переменную value необходимо инициализировать.
Допустим мы захотели изменить значение в константе value , для этого дописали ниже строку, в которой переменной value присваивается значение .
// попытка изменить значение в конста value const int value = 8; // объявление константы value = 16; // присваиваем константе value значение 16
В этом случае, компилятор сгенерирует сообщение (см. Рисунок 2).
Рисунок 2 — Квалификатор const в С++
Как видно из рисунка 2 нам не удалось изменить значение в переменной value . Вот в этом и заключается суть квалификатора const , а именно, как уже выше было сказано, квалификатор const не даёт модифицировать значение переменной, потому-то такие переменные и называются константами. Но всё это было предопределено, программистом, который писал этот фрагмент кода. То есть мы специально пытались перезаписать значение в переменной value , тогда какой смысл в квалификаторе const , если специально не пытаться изменить значение константы? А смысл такой, что квалификатор const предотвращает случайное изменение значений переменных. Случайное изменение значений не только возможно, но и трудно заметно. В итоге, программа сработала, а результат работы программы получился неправильный. Чаще всего случайное изменение значений переменных происходит из-за логических ошибок. Например, рассмотрим фрагмент кода, в котором есть скрытая логическая ошибка.
int value = 8; if (value = 1 ) // логическая ошибка в этой строке coutВ строке 1 объявлена, переменна целого типа, но не константная. В строке 2 проверяется условие в операторе условного выбора if else . От того какое условие, истинное или ложное, выполнится третья или пятая строка. Результат работы программы (см. Рисунок 3).
CppStudio.com*
Для продолжения нажмите любую клавишу . . .Рисунок 3 — Квалификатор const в С++
Как видно из рисунка 3 условие оператора условного выбора if else истинно, так как напечаталась одна звёздочка. Хотя на первый взгляд кажется, что условие ложно. Так вот, дело в том, что в условии оператора if else вместо знака отношения == стоит знак присвоить. Переменной value присваивается значение 1. А как нам уже известно, что 1- это логическая истина. Если бы переменная value была объявлена как константа, то компилятор нам бы сразу сообщил о допущенной логической ошибке.
Когда пишутся большие программы, где тысячи строк кода, то такую ошибку будет очень сложно отследить. Таким образом, если нет необходимости изменять значение переменной, то лучше подстраховаться и объявить эту переменную как константу.
К сожалению, для данной темы пока нет подходящих задач. Если у вас есть таковые на примете, отправте их по адресу: admin@cppstudio.com. Мы их опубликуем!
const
Значение констант не может быть изменено новым присваиванием, а также не может быть переопределено. Константы ( const ) подчиняются области видимости уровня блока так же, как переменные, объявленные с использованием ключевого слова let .
Синтаксис
const name1 = value1 [, name2 = value2 [, . [, nameN = valueN]]];Имя константы. Подчиняется тем же правилам, что и идентификаторы обычных переменных.
Значение константы. Может быть любым из тех типов, которые могут быть для переменных, включая функцию.
Описание
Это объявление создаёт константу, чья область действия может быть как глобальной, так и локальной внутри блока, в котором она объявлена. Глобальные константы не становятся свойствами объекта window, в отличие от var -переменных. Инициализация константы обязательна; необходимо указать значение одновременно с объявлением (смысл в том, что потом это значение изменить уже нельзя).
const создаёт константу (новую именованную ссылку на область памяти), доступную только для чтения. Это не означает, что указываемое значение неизменно, но это означает, что идентификатор не может быть переназначен. Например, если константа указывает на объект, то сам объект может быть изменён.
Все соображения по поводу временных мёртвых зон, применимы и к let и к const .
Имена констант не могут совпадать с именами функций или переменных той же области видимости.
Примеры
Ниже описаны примеры, демонстрирующие поведение const . Попробуйте их выполнить в простом JavaScript-редакторе или в консоли браузера.
// Примечание: Идентификаторы констант могут быть объявлены как в верхнем, // так и в нижнем регистре. Но правилом хорошего тона является использование // верхнего регистра. // определим MY_FAV как константу и присвоим ей значение 7 const MY_FAV = 7; // данное присваивание выдаст ошибку - Uncaught TypeError: Assignment to constant variable. MY_FAV = 20; // напечатает 7 console.log("my favorite number is: " + MY_FAV); // попробуем переопределить константу, будет брошено исключение - Uncaught SyntaxError: Identifier 'MY_FAV' has already been declared const MY_FAV = 20; // имя MY_FAV зарезервировано константой выше, данная операция // выкинет исключение var MY_FAV = 20; // здесь также будет ошибка let MY_FAV = 20; // Важно отметить свойства области видимости уровня блока if (MY_FAV === 7) // Всё нормально. Создать константу или переменную MY_FAV можно. // (работает так же как и let при объявлении в блоке переменных, которые не const) const MY_FAV = 20; // MY_FAV теперь 20 console.log("my favorite number is " + MY_FAV); // это попадёт в глобальный контекст и выдаст ошибку var MY_FAV = 20; > // MY_FAV все ещё имеет значение 7 console.log("my favorite number is " + MY_FAV); // Выдаст ошибку, потому что константа не инициализирована - Uncaught SyntaxError: Missing initializer in const declaration const FOO; // const также работает с объектами const MY_OBJECT = "key": "value">; // Попытка переопределить ссылку на объект вызовет исключение - Uncaught TypeError: Assignment to constant variable. MY_OBJECT = "OTHER_KEY": "value">; // но, свойства объекта (ключи) можно изменять MY_OBJECT.key = "otherValue"; // Используйте Object.freeze() для того, чтобы сделать объект неизменяемым // То же самое применимо к массивам const MY_ARRAY = []; // Например, можно добавлять элементы в массив MY_ARRAY.push("A"); // ["A"] // Но менять ссылку на объект массива нельзя. Это вызовет исключение - Uncaught TypeError: Assignment to constant variable MY_ARRAY = ["B"]Спецификация
Specification |
---|
ECMAScript Language Specification # sec-let-and-const-declarations |
Поддержка браузерами
BCD tables only load in the browser
Смотрите также
- var
- let
- Константы в справочнике JavaScript
Const c что это
Ключевое слово const обозначает, что та или иная величина не изменяется. Здесь приводятся подробные правила употребления описателя const.
1. Если в начале описания переменной стоит описатель const, то описываемый объект во время выполнения программы не изменяется:
const double pi = 3.1415; const Complex one(1,1);
2. Если const стоит перед определением указателя или ссылки, то это означает, что не изменяется объект, на который данный указатель или ссылка указывает:
const char* ptr = &string; //указатель на неизменяемую строку char x = *ptr; // обращение по указателю — допустимо ptr++; // изменение указателя — допустимо *ptr = '0'; // попытка изменения объекта, на который указатель указывает – ошибка
3. Если нужно объявить указатель, значение которого не изменяется, то такое объявление выглядит следующим образом:
char* const ptr = &string; //неизменяемый указатель char x = *ptr; // обращение по указателю – допустимо ptr++; // изменение указателя – ошибка *ptr = '0'; // изменение объекта, на который указатель указывает – допустимо
[Ссылки]
Как пользоваться const в C++?
С помощью ключевого слова const в C++ можно объявлять не только константы. Оно имеет много значений. И не лишним будем понимать каждое из них. Это позволяет писать более надежный код, указывая компилятору на ограничения, которые он должен проверять. Таким образом, любая попытка неправильного использования переменной или функции, объявленной с использованием const , будет выявлена на самых ранних этапах.
Переменные и const
Наиболее простой и интуитивно понятный вариант использования const заключается в объявлении константных значений:
const int CONST_VAL = 0; // CONST_VAL = 1; приводит к ошибке на этапе компиляции
Похожего результата можно добиться с помощью макросов:
#define CONST_VAL 0
Но вариант с макросом не столь хорош, поскольку не дает возможности указать тип переменной. Из-за возможных проблем с типизацией лучше пользоваться константами, а не макросами. Более того, макросов лучше и вовсе избегать. В C++ реализована прекрасная поддержка шаблонных функций и классов, которые в большинстве случаев представляют более надежную альтернативу. Однако стоит признать, что бывают случаи, когда макросы оказываются полезными. Но сейчас речь не о них.
Таким образом, если мы объявили переменную с const , то значение должно быть присвоено сразу. Потом уже ничего не сделать. А что, если нам надо объявить константный вектор? В C++11 для этого предусмотрена специальная конструкция:
const std::vector < int >CONST_VECTOR = < 1, 2, 3, 4 >;
А что, если мы по какой-то причине не может пользоваться C++11? И в этом случае можно легко объявить константный вектор:
std::vector < int >makeVector() < std::vector< int >v; v.push_back( 1 ); v.push_back( 2 ); v.push_back( 3 ); v.push_back( 4 ); return v; > const std::vector < int >CONST_VECTOR = makeVector();
То есть для сложных типов, которые не получается заполнить в одну строку, достаточно вынести компоновку в отдельную функцию для инициализации. С другой стороны, мы могли бы объявить вектор в функции makeVector() с ключевым словом static и вместо константы использовать саму функцию. Но это уже дело вкуса:
std::vector < int >makeVector() < static std::vector< int >v; if( v.empty() ) < v.push_back( 1 ); v.push_back( 2 ); v.push_back( 3 ); v.push_back( 4 ); >return v; >
Константная ссылка объявляется схожим образом:
int x = 0; const int& xRef = x; // то же самое: int const& xRef = x; // xRef = 1; приводит к ошибке на этапе компиляции
Суть константных ссылок заключается в том, что мы получаем доступ к значению переменной, на которую сделана ссылка, но изменить ее не имеем права. Основное назначение константных ссылок заключается в передаче входных параметров функциям. Но об этом немного позже.
Для указателей существует три варианта использования const :
int x = 0; // Вариант 1 const int* xPtr1 = &x; // то же самое: int const* xPtr1 = &x; xPtr1 = NULL; // имеем право изменить то, на что будет указывать указатель // *xPtr1 = 1; но нельзя изменить значение переменной, на которую указываем // Вариант 2 int* const xPtr2 = &x; // xPtr2 = NULL; нельзя изменять то, на что будет указывать указатель *xPtr2 = 1; // но можем менять значение переменной, на которую он указывает // Вариант 3 const int* const xPtr3 = &x; // xPtr3 = NULL; ничего нельзя менять // *xPtr3 = 1;
В варианте 1 мы получили указатель, который можно использовать как более гибкую константную ссылку. Он работает почти так же, но мы можем в любой момент сослаться на другую переменную. Вариант 2 работает так же, как обычная ссылка. Значение менять можно, а указать на другую переменную не выйдет. И наконец вариант 3. Он равносилен случаю константной ссылки. То есть один раз объявили указатель и ничего больше менять не можем.
В первом и втором варианте вполне можно обойтись использованием ссылок. Особой разницы нет. Для третьего ограничиться ссылкой получится не всегда. Например:
const char* const CONST_STR = "Hello, world!";
Строку в стиле C с помощью ссылки мы объявить не сможем. Нужен указатель. И он должен быть константным, поскольку изменение содержания строки запрещено и приведет к неопределенному поведению. А второй const здесь не помешает, чтобы получить жесткую привязку указателя к заданному значению и запретить случайные присвоения:
// Ничего нельзя: // CONST_STR[ 0 ] = 'h'; // CONST_STR = "Good bye!";
Функции и const
Общее правило для применений const в функциях достаточно простое. Входные параметры лучше объявлять с ключевым словом const . Однако примитивные типы int , double , char и т.д. являются исключениями из этого правила. Это объясняется тем, что примитивные типы эффективнее передавать по значению, а не по ссылке или указателю. Причиной этому служит то, что большинство примитивных типов меньше по размеру, чем адрес в памяти, который нужно было бы передать в противном случае. Кроме того, передача по значению упрощает работу оптимизатору, поскольку выполняется прямое копирование, а не косвенные операции в адресном пространстве.
Если же вы хотите передать в функцию объект структуры или класс, то используйте константную ссылку:
struct MyStruct < int x; >; void myFunction( const MyStruct& myStruct ) < int x = myStruct.x; // можем читать // myStruct.x = 1; но не можем менять значение >
Каноничным примером на этот случай является конструктор копирования:
class MyClass < public: MyClass( const MyClass& other ); // MyClass( MyClass other ); нельзя >;
Если бы мы попытались передать в конструктор копирования не ссылку, а значение, то для инициализации этого значения нам пришлось бы вызвать конструктор копирования, который мы и хотим реализовать.
Еще const можно использовать для объявления константных функций-членов классов:
class MyClass < public: void set( int x ); int get() const; >;
У класса в примере две функции: set() и get() . Первая предназначена для установки значения, а вторая для его получения. Ключевое слово const в этом случае позволяет нам явно об этом сообщить. Причем, эта информация будет полезна и компилятору, и тем, кто будет работать с нашим классом. Ведь они будут знать, что константные функции-члены не меняют состояние класса. Можно сравнить это с флагом read-only. Вот что будет, если передать константную ссылку на объект класса MyClass в функцию:
void myFunction( const MyClass& myClass ) < // myClass.set( 1 ); нельзя ничего менять int x = myClass.get(); // а вот читать пожалуйста >
То есть объявив функцию-член get() , как константную, мы пояснили компилятору, что она не меняет состояние объекта и предназначена только для чтения. Если бы мы забыли про const , то в функции myFunction() мы бы ничего не смогли сделать с экземплярами класса MyClass , а компилятор бы выдавал ошибки при попытке вызова его функций-членов. Но если бы оказалось, что нам и правда нужно менять состояние объекта, то ключевое слово const из сигнатуры функции пришлось бы убрать. А по принятым соглашениям ссылку имело бы смысл заменить на указатель:
void myFunction( MyClass* myClass ) < myClass->set( 1 ); int x = myClass->get(); >
Но тут есть один тонкий момент. Иногда бывает полезно инкапсулировать информацию о том, что на самом деле внутреннее состояние класса меняется, но все равно объявить функцию-член константной. Например, в многопоточной среде мы можем использовать мьютексы:
class MyClass < public: MyClass() : m_x( 0 ) < >void set( int x ) < std::lock_guard< std::mutex >lock( m_mutex ); m_x = x; > int get() const < // Не будет работать, потому что меняется одно из полей класса: // std::lock_guard< std::mutex >lock( m_mutex ); return m_x; > private: int m_x; std::mutex m_mutex; >;
И что же делать? - Для этого в C++ предусмотрено ключевое слово mutable . Если мы объявим поле мьютекса, как mutable , то укажем компилятору, что состояние объекта может меняться даже в константных функциях-членах:
class MyClass < public: MyClass() : m_x( 0 ) < >void set( int x ) < std::lock_guard< std::mutex >lock( m_mutex ); m_x = x; > int get() const < std::lock_guard< std::mutex >lock( m_mutex ); // теперь все работает return m_x; > private: int m_x; mutable std::mutex m_mutex; >;
Заключение
Вот мы и разобрались с ключевым словом const в С++. Оно имеет довольно много значений и позволяет улучшить надежность и читаемость кода, если правильно им пользоваться. Поэтому я нахожу довольно странным, что в большинстве других языков программирования, которые мне известны, хоть и есть некие аналоги const , но имеют в них ограниченную область применения. Более того, я считаю, что в C++ реализована одна из лучших возможных схем управления константами.