const — это сокращение от constant, изначально означающее неизменный и непреложный. В C++ он используется для модификации переменных встроенных типов, пользовательских объектов, функций-членов, возвращаемых значений и параметров функций.

C++ const позволяет задать семантическое ограничение, которое проверяет компилятор, позволяя программисту сообщить компилятору, что значение остаётся неизменным. Если во время программирования действительно есть значение, которое остаётся неизменным, следует явно использовать const, чтобы получить помощь компилятора.

1. const модифицирует переменные обычных типов

const int  a = 7; 
int  b = a; // 正确
a = 8;       // 错误,不能改变

a определена как константа, и a можно присвоить b, но a нельзя присвоить снова. Присваивание константе недопустимо, поскольку компилятор считает a константой, и её значение нельзя изменять.

Теперь рассмотрим следующие операции:

Пример

#include<iostream> using namespace std; int main(void) { const int a = 7; int *p = (int*)&a; *p = 8; cout<<a; system("pause"); return 0; }

Для const-переменной a мы берём адрес переменной, приводим его и присваиваем указателю на int, затем используем *p = 8;, чтобы заново присвоить значение по адресу переменной a, а затем выводим результат, чтобы проверить значение a.

Из окна отладки ниже видно, что значение a изменено на 8, но результат вывода по-прежнему равен 7.

По результату видно, что компилятор затем считает значение a равным 7, как было изначально определено, поэтому операции с const a приводят к описанной выше ситуации. Следовательно, никогда не пытайтесь присваивать значение const-переменной, так как это приведёт к неожиданному поведению.

Если вы не хотите, чтобы компилятор замечал описанные выше операции с const, можно добавить ключевое слово volatile перед const.

Ключевое слово volatile противоположно const; оно означает изменяемый и легко изменяемый. Поэтому оно не будет оптимизировано компилятором, и компилятор не будет изменять свои операции с переменной a.

Пример

#include<iostream> using namespace std; int main(void) { volatile const int a = 7; int *p = (int*)&a; *p = 8; cout<<a; system("pause"); return 0; }

Результат вывода равен 8, как мы и ожидали.

2. const изменяет переменные-указатели

Модификация переменных-указателей с помощью const имеет следующие три случая.

  • A: Если const изменяет то, на что указывает указатель, то содержимое неизменяемо.
  • B: Если const изменяет указатель, то сам указатель неизменяем.
  • C: Если const изменяет и указатель, и то, на что он указывает, то и указатель, и содержимое, на которое он указывает, неизменяемы.

Для A:

const int *p = 8;

Тогда содержимое, на которое указывает указатель, 8, нельзя изменить. Короче говоря, «слева фиксируется значение», потому что const находится слева от знака *.

Для B:

int a = 8;
int* const p = &a;
*p = 9; // 正确
int  b = 7;
p = &b; // 错误

Для указателя const p адрес памяти, на который он указывает, нельзя изменить, но его содержимое можно изменить. Короче говоря, «справа фиксируется направление», потому что const находится справа от знака *.

Для C:Это комбинация A и B.

int a = 8;
const int * const  p = &a;

В этом случае и содержимое, на которое указывает const p, и адрес памяти, на который он указывает, фиксированы и не могут быть изменены.

Для трёх случаев A, B и C, в зависимости от различного положения const относительно знака *, я суммирую три фразы для лёгкого запоминания:«Слева фиксируется значение, справа фиксируется направление, const изменяет инварианты».。

3. Передача параметров const и возвращаемые значения функций

Модификация параметров функции с помощью const делится на три случая.

A: const изменяет передачу по значению. Обычно в этом случае не требуется модификация const, потому что функция автоматически создаёт временную переменную для копирования значения аргумента.

Пример

#include<iostream> using namespace std; void Cpf(const int a) { cout<<a; // ++a; неверно, a нельзя изменить } int main(void) { Cpf(8); system("pause"); return 0; }

B: Когда параметр const является указателем, это может предотвратить случайное изменение указателя.

Пример

#include<iostream> using namespace std; void Cpf(int *const a) { cout<<*a<<" "; *a = 9; } int main(void) { int a = 8; Cpf(&a); cout<<a; // a равно 9 system("pause"); return 0; }

C: При передаче параметров пользовательских типов требуется временный объект для копирования параметра. Создание временного объекта требует вызова конструктора, что является пустой тратой времени. Поэтому мы используем передачу по const-ссылке.

Более того, для обычных встроенных типов, таких как int и double, мы не используем передачу по ссылке.

Пример

#include<iostream> using namespace std; class Test { public: Test(){} Test(int _m):_cm(_m){} int get_cm()const { return _cm; } private: int _cm; }; void Cmf(const Test& _tt) { cout<<_tt.get_cm(); } int main(void) { Test t(8); Cmf(t); system("pause"); return 0; }

Вывод результата8。

Для const, изменяющего возвращаемое значение функции.

Модификация возвращаемых значений с помощью const делится на три случая.

A: const, изменяющий возвращаемое значение встроенного типа, имеет тот же эффект, что и без него.

Пример

#include<iostream> using namespace std; const int Cmf() { return 1; } int Cpf() { return 0; } int main(void) { int _m = Cmf(); int _n = Cpf(); cout<<_m<<" "<<_n; system("pause"); return 0; }

B: const модифицирует пользовательский тип как возвращаемое значение. В этом случае возвращаемое значение нельзя использовать как lvalue; его нельзя присвоить или изменить.

C: const модифицирует возвращаемый указатель или ссылку. Возвращать ли указатель на const, зависит от того, что мы хотим, чтобы делал пользователь.

4. const модифицирует функции-члены класса

Модификатор const для функции-члена класса предназначен для того, чтобы предотвратить изменение функцией-членом значения вызываемого объекта. Если мы не хотим изменять значение вызывающего объекта, все функции-члены должны быть объявлены как функции-члены const.

Примечание:Ключевое слово const не может использоваться вместе с ключевым словом static, потому что ключевое слово static модифицирует статические функции-члены, а статические функции-члены не содержат указателя this, т.е. они не могут быть инстанцированы. Функция-член const должна быть привязана к конкретному экземпляру.

Следующая функция get_cm()const; использует функцию-член const:

Пример

#include<iostream> using namespace std; class Test { public: Test(){} Test(int _m):_cm(_m){} int get_cm()const { return _cm; } private: int _cm; }; void Cmf(const Test& _tt) { cout<<_tt.get_cm(); } int main(void) { Test t(8); Cmf(t); system("pause"); return 0; }

Если get_cm() убирает модификатор const, то даже если const _tt, переданный в Cmf, не изменяет значение объекта, компилятор всё равно считает, что функция изменит значение объекта. Поэтому мы должны, как требуется, делать все функции, которым не нужно изменять содержимое объекта, функциями-членами const.

Что делать, если функция-член хочет изменить конкретный член объекта? В этом случае мы можем использовать ключевое слово mutable для изменения этого члена. mutable также означает изменяемый и легко изменяемый. Член, модифицированный ключевым словом mutable, может находиться в постоянном состоянии изменения, как в следующем примере.

Пример

#include<iostream> using namespace std; class Test { public: Test(int _m,int _t):_cm(_m),_ct(_t){} void Kf()const { ++_cm; // Неправильно ++_ct; // Правильно } private: int _cm; mutable int _ct; }; int main(void) { Test t(8,7); return 0; }

Здесь, в Kf()const, мы изменяем значение _ct с помощью ++_ct;, но изменение _cm с помощью ++_cm вызовет ошибку, потому что ++_cm не помечен как mutable.

Адрес оригинальной статьи: https://www.cnblogs.com/Forever-Kenlen-Ja/p/3776991.html