Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих.pdf
Скачиваний:
183
Добавлен:
01.05.2014
Размер:
3.97 Mб
Скачать

(a) i = ic;

(d) pic =

cpic;

 

(b)pic = ⁣ (i) cpic = ⁣

(c)cpi = pic; (f) ic = *cpic;

3.6.Ссылочный тип

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

Ссылочный тип обозначается указанием оператора взятия адреса (&) перед именем

int ival = 1024;

//правильно: refVal - ссылка на ival int &refVal = ival;

//ошибка: ссылка должна быть инициализирована

переменной. Ссылка должна быть инициализирована. Например: int &refVal2;

Хотя, как мы говорили, ссылка очень похожа на указатель, она должна быть инициализирована не адресом объекта, а его значением. Таким объектом может быть и

int ival = 1024;

// ошибка: refVal имеет тип int, а не int*

int &refVal = &ival;

int *pi = &ival;

// правильно: ptrVal - ссылка на указатель

указатель:

int *&ptrVal2 = pi;

Определив ссылку, вы уже не сможете изменить ее так, чтобы работать с другим объектом (именно поэтому ссылка должна быть инициализирована в месте своего определения). В следующем примере оператор присваивания не меняет значения refVal, новое значение присваивается переменной ival – ту, которую адресует refVal.

int min_val = 0;

//ival получает значение min_val,

//а не refVal меняет значение на

min_val refVal = min_val;

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

refVal += 2;

прибавляет 2 к ival – переменной, на которую ссылается refVal. Аналогично

int ii = refVal;

присваивает ii текущее значение ival,

int *pi = &refVal;

инициализирует pi адресом ival.

Если мы определяем ссылки в одной инструкции через запятую, перед каждым объектом типа ссылки должен стоять амперсанд (&) – оператор взятия адреса (точно так же, как и

//определено два объекта типа int int ival = 1024, ival2 = 2048;

//определена одна ссылка и один объект int &rval = ival, rval2 = ival2;

//определен один объект, один указатель и одна

ссылка

int inal3 = 1024, *pi = ival3, &ri = ival3;

// определены две ссылки

для указателей). Например:

int &rval3 = ival3, &rval4 = ival2;

Константная ссылка может быть инициализирована объектом другого типа (если, конечно, существует возможность преобразования одного типа в другой), а также

double dval = 3.14159;

// верно только для константных ссылок

const int &ir = 1024; const int &ir2 = dval;

безадресной величиной – такой, как литеральная константа. Например:

const double &dr = dval + 1.0;

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

Для литералов это более или менее понятно: у нас не должно быть возможности косвенно поменять значение литерала, используя указатели или ссылки. Что касается объектов другого типа, то компилятор преобразует исходный объект в некоторый

double dval = 1024;

вспомогательный. Например, если мы пишем: const int &ri = dval;

int temp =

dval

;

то компилятор преобразует это примерно так: const int &ri = temp;

Если бы мы могли присвоить новое значение ссылке ri, мы бы реально изменили не dval, а temp. Значение dval осталось бы тем же, что совершенно неочевидно для программиста. Поэтому компилятор запрещает такие действия, и единственная возможность проинициализировать ссылку объектом другого типа – объявить ее как const.

Вот еще один пример ссылки, который трудно понять с первого раза. Мы хотим определить ссылку на адрес константного объекта, но наш первый вариант вызывает

const int ival = 1024;

// ошибка: нужна константная ссылка

ошибку компиляции:

int *&pi_ref = &ival;

const int ival = 1024;

// все равно ошибка

Попытка исправить дело добавлением спецификатора const тоже не проходит: const int *&pi_ref = &ival;

В чем причина? Внимательно прочитав определение, мы увидим, что pi_ref является ссылкой на константный указатель на объект типа int. А нам нужен неконстантный указатель на константный объект, поэтому правильной будет следующая запись:

const int ival = 1024;

// правильно

int *const &piref = &ival;

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

int *pi = 0;

мы инициализируем указатель pi нулевым значением, а это значит, что pi не указывает ни на какой объект. В то же время запись

const int &ri = 0;

int temp =

0

;

означает примерно следующее: const int &ri = temp;

int ival = 1024, ival2 = 2048; int *pi = &ival, *pi2 =

&ival2;

Что касается операции присваивания, то в следующем примере: pi = pi2;

переменная ival, на которую указывает значение адреса переменной ival2. И pi, и объект ival2.

pi, остается неизменной, а pi получает pi2 и теперь указывают на один и тот же

int &ri = ival, &ri2 = ival2;

Если же мы работаем со ссылками: ri = ri2;

то само значение ival меняется, но ссылка ri по-прежнему адресует ival.

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

//пример использования ссылок

//Значение возвращается в параметре next_value

bool get_next_value( int &next_value );

// перегруженный оператор

Matrix operator+( const Matrix&, const Matrix& );

Как соотносятся самостоятельные объекты-ссылки и ссылки-параметры? Если мы

int ival;

пишем:

while (get_next_value( ival )) ...

это равносильно следующему определению ссылки внутри функции:

int &next_value = ival;

(Подробнее использование ссылок в качестве формальных параметров функций рассматривается в главе 7.)

Упражнение 3.19

(a) int ival = 1.01;

(b) int &rval1 = 1.01;

(c) int &rval2 = ival;

(d) int &rval3 =

&ival;

(f) int &rval4 = pi;

(e) int *pi = &ival;

(g) int &rval5 = pi*;

(h) int &*prval1 = pi;

Есть ли ошибки в данных определениях? Поясните. Как бы вы их исправили?

(i) const int &ival2 = 1; (j) const int &*prval2 = &ival;

Упражнение 3.20 Если ли среди нижеследующих операций присваивания ошибочные (используются

(a)rval1 = 3.14159;

(b)prval1 =

prval2;

(c) prval2 = rval1;

определения из предыдущего упражнения)?

(d) *prval2 = ival2;

Упражнение 3.21 Найдите ошибки в приведенных инструкциях: