Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по С++ глава 2.doc
Скачиваний:
13
Добавлен:
05.11.2018
Размер:
249.86 Кб
Скачать

2.3.8. Константы

Используя ключевое слово const, можно определить переменную для хранения значения константы.

const int MaxLines = 100;

Примечание

Объект типа const иногда называют именованной константой, а не переменной типа константа, так как употребление термина переменная в данном случае неправомерно.

Для определения константной переменной (типа const) ее необходимо инициализировать. Инициализация, выполняемая один раз, является единственно возможным способом задания значения константной переменной, которое нельзя изменить, используя операцию присваивания. Следующий код иллюстрирует допустимые и недопустимые операции с константами.

const double CD = 2.5;

double D;

D = CD; // для чтения константы

CD = 5.0; // СШИБКА: нельзя присвоить новое значение константе

++CD; // ОШИБКА: нельзя изменить значение типа const

Константную переменную можно инициализировать, используя как константное выражение (например, 5), так и другую переменную. Например, все приведенные ниже определения константных переменных являются корректными (первые два инициализируют константу с константными выражениями, а вторые два – с другими переменными).

void Func (int Farm)

{

int I = 3;

const int CI1 = 5;

const int CI2 = 2 * sizeof(float);

const int CI3 = I;

const int CI4 = Parm;

}

Если константная переменная инициализирована выражением, содержащим другие переменные, то ее нельзя использовать для определения размерности массива. (Этот метод может оказаться некорректным для динамически создаваемого массива, размер которого определяется во время выполнения. В последнем параграфе главы проанализированы корректные методы выделения памяти динамическим массивам.) Например:

void Func (int Farm)

{

const int CI1 = 100;

const int CI2 = Parm;

char Bufl [CIl]; // правильно

char Buf2 [CI2]; // ОШИБКА: требуется константное выражение

}

Константные переменные используются так же, как символические константы, определяемые директивой препроцессора #define и традиционно применяемые в программах на языке С. Там, где требуется константное выражение, нужно инициализировать константную переменную константным выражением. Подобно константе, определяемой оператором #define, константная переменная может быть определена в файле заголовков, включенном в один или более исходных файлов, составляющих программу. В отличие от неконстантной переменной, константная по умолчанию является локальной для файла, в котором она определена. Иногда она может быть определена более чем в одном исходном файле, что не приводит к появлению ошибки при компоновке программы.

Примечание

Константная переменная имеет преимущества перед константой, определенной оператором #define, так как к ней можно обратиться с помощью символического отладчика.

Константы и указатели

Существует несколько способов использования ключевого слова const при объявлении указателя.

Во-первых, можно определить указатель на целочисленную константу:

const int *PCInt;

Допускается свободно изменять значение такого указателя, но не значение переменной, на которую он указывает, например:

const int A = 1; int В = 2;

const int *PCInt; //не нужно инициализировать. PCInt не имеет тип const

PC Int = &А;

*PCInt =5; // СШИБКА: нельзя изменять переменную

PCInt = &В; // можно изменять PCInt

Обратите внимание: указателю PCint может быть присвоен адрес целочисленной переменной, которая является константой или таковой не является. Однако, даже если этому указателю присвоен адрес переменной, не являющейся константой, его нельзя использовать для изменения переменной (в этом случае указатель служит для доступа «только для чтения» к переменной, допускающей чтение и запись).

Во-вторых, можно определить константный указатель на переменную, не являющуюся константой, например:

int N;

int *const CPInt = &N;

Так как этот указатель сам является константой, он должен быть инициализирован при определении, и впоследствии ему нельзя присваивать другой адрес, но его можно использовать для изменения значения переменной, на которую он указывает, например:

int A = 1; int В = 2;

int *const CPInt = &А; // нужно инициализировать указатель CPInt

*CPInt = 5; // можно изменить переменную указатель

CPInt = &В; // ОШИБКА: нельзя изменить указатель типа const

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

Наконец, отметим, что нельзя присвоить адрес константной переменной указателю на переменную-неконстанту. В этом случае выбирается обходной путь изменения переменной, например:

const int N = 1;

int *PInt;

Pint = &N; // СШИБКА: нельзя присвоить адрес целочисленной

// константы указателю на целочисленную неконстанту

*PInt =2; // Если бы присваивание было допустимо, это выражение

// привело бы к изменению переменной-константы!

Константы и ссылки

Можно также определить ссылку на константный объект. Ссылки этого типа можно инициализировать, используя константную переменную

const int A = 1;

const int &RCIntA = A;

или переменную-неконстанту

int В;

const int &RCIntB = В;

В обоих случаях ссылку нельзя использовать для изменения значения пере­менной, на которую она ссылается.

const int А = 1;

const int SRCIntA = A;

int B;

const int &RCIntB = B;

RCIntA = 5; //ОШИБКА: нельзя изменять значение ссыпки на константу RCIntB = 10; //ОШИБКА: нельзя изменять значение ссылки на константу

Если константная ссылка (т.е. ссылка на переменную типа const) инициализирована с использованием переменной-неконстанты, то она служит для этой переменной псевдонимом, предназначенным «только для чтения». Это и есть тот редкий случай, когда ссылка ведет себя не так, как переменная, на которую она ссылается.

Кроме того, можно инициализировать константную ссылку, используя константное выражение (вспомните, что нельзя инициализировать ссылку на переменную-неконстанту константным выражением), например:

const int &RCInt = 5; // правильная инициализация

В этом определении компилятор создает временную переменную типа const int, содержащую значение 5, а затем инициализирует PCint как псевдоним этой временной переменной.

Обратите внимание: бессмысленно (хотя и допустимо) объявлять ссылочную переменную константой, поскольку все ссылки автоматически являются константами (вспомните, что после инициализации ссылки ее нельзя сделать ссылкой на другую переменную).

int N;

int &const RClnt = N; // разрешено, но бессмысленно

Наконец, нельзя использовать константную переменную для инициализации ссылки на не константный тип данных, потому что это косвенный способ изменения константной переменной.

const int CInt = 1;

int &RInt = Cint; // ошибка

RInt =5; // Если бы инициализация была допустима, это выражение

// привело бы к изменению константной переменной!

Константы и функции

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

Объявление параметра как константы означает, что функция не может изменить значение этого параметра, что касается лишь деталей реализации функции. Поэтому при вызове на данную особенность можно не обращать внимания.

void FuncA (const int N); // FuncA не может изменить N; ну и что?

Если параметр является указателем или ссылкой, то функция может, как обычно, изменять значение передаваемой переменной,

FuncA {int *PInt); FuncB (int &RInt) ;

void main ()

{

int N = 1;

FuncA (&N); // FuncA может изменять значение N

FuncB (N); // FuncB может изменять значение N

// ...

}

Если же параметр является указателем или ссылкой на тип const, то функция не может изменить значение передаваемой переменной. При вызове функции побочный эффект, вызванный изменением значения переменной, отсутствует. Например:

FuncA (const int *PInt);

FuncB (const int &RInt};

void main ()

{

int N = 1;

FuncA (&N); // FuncA HE МОЖЕТ изменить значение N

FuncB (N); // FuncB HE МОЖЕТ изменить значение N

// ...

}

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

const int N = 1;

можно законно передать адрес N в FuncA или передать N в FuncB, как в приведенном выше примере.

Если параметр объявлен как ссылка на константу, то можно передать функции константное выражение (не допустимо для параметра, который является ссылкой на неконстанту).

void FuncA (const int &RInt);

void main()

{

FuncA (5); // разрешено

// ...

}

Если функция возвращает значение базового типа (например, int или double), добавление ключевого слова const к спецификации возвращаемого значения в определении функции не имеет особого значения, потому что изменить такое возвращаемое значение нельзя никаким способом (т. е. это не адресуемое выражение).

Например:

const int Func(); // Возвращается не адресуемое выражение,

// поэтому его никак нельзя изменить

Однако если функция возвращает указатель или ссылку, добавление ключевого слова const означает, что вызывающая функция не может использовать возвращаемое значение для изменения значения переменной, на которую указывает или ссылается функция. Например:

const int *FuncA()

{

static int Protected = 1;

++Protected;

return SProtected;

}

const int &FuncB ()

{

static int Safe = 100;

--Safe;

return Safe;

}

void main {int Parm)

{

int N;

N = *FuncA(); //разрешено: N принимает копию Protected

N = FuncB(); //разрешено: N принимает копию переменной Safe

*FuncA() =5; //ОШИБКА: попытка изменения значения типа const

++FuncB (); //ОШИБКА: попытка изменения значения типа const

}

Заметьте, что сами функции FuncA и FuncB изменяют значение внутреннего элемента данных. Так как в их объявлении имеется ключевое слово const, вызывающей функции запрещено производить аналогичные изменения.