Си++: различия между версиями

Содержимое удалено Содержимое добавлено
Нет описания правки
Нет описания правки
Строка 40:
 
Использование ссылок очень похоже на использование указателей, только вместо ''звёздочки'' «<tt>*</tt>» нужно писать ''амперсанд'' «<tt>&amp;</tt>».
 
Наш пример будет теперь выглядеть вот так:
void foo (int &x) {
x = 17;
}
void main () {
int z = 5;
foo (z);
/* z теперь равно 17 */
}
 
Что изменилось? Аргумент функции <tt>foo</tt> стал не указателем, а ссылкой. Поэтому теперь:
* при вызове функции <tt>foo</tt> компилятор сам передаст ''адрес'' переменной <tt>z</tt>, нет необходимости специально его просить;
* внутри функции <tt>foo</tt> мы обращаемся с <tt>x</tt>, как с обычной переменной, и только компилятор знает, что внутри это — указатель.
 
==== Что дают ссылки ====
 
Есть в программировании такое понятие, как ''совмещение имён'' (по-английски — ''aliasing''). Вернёмся к трём нашим примерам (приведу по первой строчке из каждого):
 
; <tt>void foo (int x)</tt> : Здесь имена <tt>x</tt> и <tt>z</tt> обозначают совершенно разные вещи. (Если хотите, это названия разных ячеек памяти.)
 
; <tt>void foo (int *x)</tt> : Здесь <tt>*x</tt> и <tt>z</tt> — два названия ''одного и того же''. Есть в памяти место, которое внутри функции <tt>main</tt> назвается <tt>z</tt>, а внутри <tt>foo</tt> — <tt>*x</tt>.
 
; <tt>void foo (int &x)</tt> : Здесь уже просто <tt>x</tt> и <tt>z</tt> — два названия одной и той же области памяти. Хотя по внешнему виду <tt>x</tt> невозможно её отличить от обычной переменной, для компилятора это — просто указатель с другим лицом.
 
==== Чем отличаются ссылки от переменных ====
 
Пусть мы написали:
int z;
int &x = z;
 
После этого <tt>z</tt> и <tt>x</tt> обозначают одно и то же — некую область в памяти, в которой хранится целое число. Заметим, что эти две строки принципиально отличаются.
 
Во-первых, мы можем написать
int z = 7;
а можем
int z;
z = 7;
Это будет практически одно и то же.
 
Ссылки ведут себя совсем иначе. Запись
int &x = z;
означает, что «<tt>x</tt> отныне является ссылкой на область памяти, которая раньше называлась <tt>z</tt>». В то же время запись
x = z;
означала бы, что области памяти, на которую ссылается <tt>x</tt>, нужно присвоить значение переменной <tt>z</tt>. В нашем случае (когда <tt>x</tt> ссылается на <tt>z</tt>) это эквивалентно записи
z = z;
 
Заметим, что когда мы пишем
int z;
то выделяется память для хранения величины типа <tt>int</tt>. А когда мы пишем
int &x = z;
мы пользуемся «чужой» памятью, то есть памятью, выделенной когда-то кем-то.
 
Когда время жизни переменной <tt>z</tt> заканчивается (в нашем примере — когда мы возвращаемся из <tt>main()</tt>), выделенная память освобождается. Если на неё остались какие-то указатели и ссылки — это ваши проблемы, и ваша программа непременно вскоре упадёт. У нас такого не происходит, потому что пока жива ссылка <tt>x</tt>, жива и переменная <tt>z</tt>.
 
==== Отличие ссылок от указателей ====
 
По сути ссылка — это указатель, который
* обязательно при создании инициализировать каким-то значением;
* нельзя изменять после этого.
 
Например, вот так написать вообще нельзя:
int &x;
Ссылку обязательно инициализировать! Подойдёт любой из следующих способов:
int z;
int *pz = &z;
int &x1 = z;
int &x2 = *pz;
int &x3 = * (int*) malloc (sizeof (int)); /* но это извращение */
int &x4 = *new int; /* точно такое же извращение */
int &x5 = x1; /* можно инициализировать и через другую ссылку */
 
Ссылку, как только что было сказано, нельзя заставить ссылаться на другой объект. Раз уж она начила на что-то ссылаться, то она будет на это ссылаться до конца жизни. Например, заведём указатель и ссылку и попытаемся их поменять:
int z = 3, zz = 5;
int *pz = &z; /* теперь pz указывает на z, *pz == z == 3 */
int &x = z; /* теперь x указывает на z, x == z == 3 */
pz = &zz; /* теперь pz указывает на zz, *pz == zz == 5 */
x = zz; /* но x ведь указывала на z! значит, мы написали «z = zz».
теперь x == z == zz == 5 */
Изменить указатель, скрытый за ссылкой <tt>x</tt>, нельзя — просто нет в Си++ оператора, позволяющего это сделать.