Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lek_1_slaydy.doc
Скачиваний:
1
Добавлен:
17.11.2019
Размер:
417.79 Кб
Скачать

3: Main()

4: {

5: int ivar = 1234;

// Переменной присвоено значение

6: int *iptr = &ivar;

// Указателю присвоен адрес ivar

7: int &iref = ivar;

// Ссылка ассоциирована с ivar

8: int *p = &iref;

// Указателю присвоен адрес iref

9:

10: cout « "ivar == " « ivar « '\n';

11: cout « "*iptr == " « *iptr « '\n';

12: cout « "iref == " « iref « '\n';

13: cout « "*p == " << *p « '\n';

14: Return 0;

15:}

В строках 5-8 объявляются четыре переменные. Первая — простое целое ivar, инициализированное значением 1234. Следующая, — указатель на целое с именем iptr, которой присвоен адрес ivar. Третья объяв­лена как ссылочная переменная. Строка

int &iref = ivar;

объявляет iref как ссылку на целое значение. Что касается iref, то ей присваивается ivar не как значение пере­менной, а как адрес в памяти. Следуя объявлению, iref указывает на ivar подобно указателю. Как показано в строке 12, оператор

cout « "iref == " « iref « '\n';

выводит значение ivar — 1234. Это происходит благодаря использованию iref в качестве ссылки на местополо­жение переменной ivar в памяти.

Четвертое объявление в строке 8 создает еще один указатель — p, которому присваивается адрес, храня­щийся в iref. Строки 6 и 8 дают одинаковый результат. В обеих строках создаются указатели, ссылающиеся на ivar. Как показано в строке 13, употребление указателя в выражении *p дает доступ к значению ivar. На рис. 1.2 проиллюстрирована взаимосвязь переменных из листинга 1.15. Как видно из иллюстрации, исполь­зование iref очень похоже на использование разыменования указателя *p. В обоих выражениях осуществляет­ся доступ к значению ivar — 1234.

Рис. 1.2. Взаимосвязь переменных в листинге 1.15, REFVALCPP

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

В REFVAL.CPP вы не можете объявить новую целочисленную переменную и при­своить ее адрес iref:

int anotherInt;

iref = anotherInt; // ???

Это присваивание не заставит iref ссылаться на anotherInt. Это выражение присвоит значение anotherInt объекту, на который iref ссылается, другими словами — ivar. Так же вы можете присваивать новые значения объекту, к которому относится ссылка. Например, выражение

iref = 4321;

присвоит ivar значение 4321. Эти правила будет легче запомнить, если представить себе ссылки как неизме­няемые альтернативные имена, похожие на указатели (но не идентичные им!).

Как следует из вышесказанного, ссылки должны быть инициализированы при их объявлении. Строка 7, к примеру, не может быть написана как

int &iref; // ???

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

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

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

Добавьте между строками 13 и 14 листинга 12.15 следующий оператор:

iref++;

Компилятор сгенерирует код, инкрементирующий ivar — переменную, на которую ссылается iref. Оператор не совершит никаких действий непосредственно над iref.

Ссылки также могут ссылаться на константы.

Разумеется, вы не можете присвоить значение непосредст­венно ссылке. Объявление

int &iref = 1234; // ???

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

int x = 1234; // Объявление и инициализация целочисленного объекта

const int &iref = x; // Объявление iref как константной ссылки на x

Этот прием очень полезен, поскольку эффективно превращает переменную в константу, которую нельзя подвергнуть изменениям. Например, вы можете записать значение x в выходной поток с помощью ссылки сле­дующим образом:

cout << "iref = " << iref << '\n'; // Все в порядке

Вы также можете изменить значение переменной x:

x = 4321; // Тоже годится

Поскольку iref — константа, то вы, конечно, не сможете присвоить новое значение x через ссылку:

iref = 4321; // ??? Нельзя

Попытка скомпилировать последнее выражение приведет к выдаче сообщения об ошибке "Cannot modify a const object in function main()" (Нельзя изменить константный объект в функции main()).

Ссылки в качестве параметров функций

Cами по себе ссылочные переменные используются довольно редко.

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

Листинг 1.16 —введите сумму платежей и налогов после их запроса. Программа выведет цену и сум­му налога на введенные вами значения.

Листинг 1.16. TAX.CPP (использование ссылок в качестве параметров функции)

1: #include <iostream.h>

2:

3: #include <stdlib.h>

4: #include <stdio.h>

5:

6: double GetDouble(const char *prompt);

7: void GetData(double &paid, double &rate);

8: void Calculate(const double paid, const double rate,

9: double &list, double &tax);

10:

11: main()

12: {

13: double paid, rate, list, tax; 14: GetData(paid, rate);

15: Calculate(paid, rate, list, tax);

16: printf("List pride = $%8.2f\n", list); 17: printf("Tax paid = $%8.2f\n", tax);

18: return 0;

19:} 20:

21: // Возвращает одно вещественное значение двойной точности

22: double GetDouble(const char *prompt)

23: {

24: char s[20]; // Строка для ввода

25: printf(prompt); // Отображение строки запроса

26: scanf("%20s", s); // Ввести данные как строку

27: return atof(s); // Возвратить введенные данные

28: }

29:

30: // Ввод цены и ставки налога вещественными значениями двойной точности

31: // Замечание: изменяет значение paid и rate

32: void GetData(double &paid, double &rate)

33: {

34: paid = GetDouble("Price paid?");

35: rate = GetDouble("Tax rate (ex: .06)?");

36: }

37:

38: // Вычисляет цену и налог no сумме и ставке

39: // Замечание: изменяет значение list и tax

40: void Calculate(const double paid, const double rate,

41: double &list, double &tax)

42: {

43: list = paid / (1 + rate);

44: tax = paid - list;

45:}

В программе определены три функции. Первая функция запрашивает и возвращает вещественное значение двойной точности — обычное описание функции, возвращающей некоторое значение:

double GetDouble(const char *prompt);

Вторая функция демонстрирует использование ссылочных параметров для возврата двух или более значений. Заметьте, что обоим параметрам функции предшествует символ &, означающий, что эти параметры передаются по ссылке (т.е. в GetData передаются их адреса вместо значений):

void GetData (double &paid, double &rate);

Рассмотрим тело функции GetData. В нем дважды вызывается GetDouble с аргументом-строкой, которая должна отображаться, и полученные значения присваиваются ссылочным параметрам paid и rate:

paid - GetDouble("Price paid?");

rate = GetDouble("Tax rate (ex: .06)?");

Поскольку paid и rate — ссылки, значения присваиваются объектам, переданным функции GetDouble(). На­пример, в функции main() следующий оператор вводит значения в две переменные, также названные paid и rate:

GetData(paid, rate);

Некоторые критикуют передачу параметров функции по ссылке, верно замечая, что такие вызовы выглядят обманчиво, поскольку аргументы в них передаются посылке неявно. Ключ к решению этой проблемы и эффективному использованию ссылочных параметров — хорошее документирование программ. Обязательно должен присутствовать комментарий, в котором указывается, что вызов GetData изменяет значения paid и rate.

Третья, и последняя, функция в примере вычисляет цену и налог к уплате, основываясь на действительной цене и ставке налога. Первых два параметра, paid и rate, — входные параметры функции. Поскольку функ­ция не изменяет их, они объявлены константами, и передаются только их значения (точнее, копии их значений). Вторых два параметра, list и tax, — это выходные значения функции. Эти значения изменяются функцией и, следовательно, передаются по ссылке:

void Calculate(const double paid, const double rate, double &list, double &tax)

Функция Calculate присваивает новые значения list и tax в следующих операторах:

list = paid / (1 + rate);

tax = paid - list;

Поскольку параметры — ссылки, в действительности используются переменные, переданные функции Calculate(). Операция присваивания происходит по ссылке, вследствие чего изменяются действительные пере­менные, когда в функции main происходит следующий вызов:

Calculate(paid, rate, list, tax);

Аргументы, передающиеся по значению, невозможно отличить от аргументов, передающихся по ссылке. Хорошее документирование функции Calculate уменьшит опасность подобных ошибок.

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

void f(int *ip)

*ip = 1234;

Внутри этой функции осуществляется доступ к переданному аргументу, адрес которого хранится в указате­ле ip, с помощью оператора вида

f(&ivar); // Передача адреса ivar

Внутри функции выражение *ip = 1234; присваивает значение 1234 переменной ivar, адрес которой переда­ется функции f(). Теперь рассмотрим аналогичную функцию, использующую ссылочные параметры:

void f(int &ir)

{ ir = 1234;

}

Указатель ip заменен ссылкой ir, которой функция присваивает 1234. Выражение

f(ivar); // Передача ivar по ссылке

присваивает значение ссылочному объекту: передает ivar по ссылке функции f(), и поскольку ir ссылается на ivar, то ivar присваивается 1234.

Ссылки в качестве результатов функций

Функции могут возвращать ссылки на объекты при условии, что эти объекты существуют, когда функция неактивна. (Другими словами, функции не могут возвращать ссылки на локальные автоматические перемен­ные).

Для функции, объявленной как

double &ref(double d);

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

Листинг 1.17 демонстрирует использование ссылочных функций для скрытия внутреннего представления структуры данных. Это используется при реализации больших проектов, особенно когда структуры данных могут изменяться, что, в свою очередь, может послужить причиной коренной переделки программ, написан­ных на обычном С. Ссылочные функции в С++ могут облегчить этот рутинный процесс, обеспечив дос­туп к структурам данных, подобным массивам, без привязки кода к физическим свойствам данных. Скомпи­лируйте и запустите программу, по приглашению введите индексное значение от 0 до 9, затем — вещественное значение. Для завершения работы программы нажмите по приглашению <Enter>.

Листинг 1.17. REFFUNC.CPP (ссылочные функции)

1 : #include <iostream.h>

2: #include <stdlib.h>

3: #include <string.h>

4:

5: #define FALSE 0

6: #define TRUE 1

7: #define SIZE 10

8: #define BUFLEN 20

9:

10: double &ref(int index);

11: void ShowArray(void); 12:

13: double array[SIZE]; 14:

15: main()

16: {

17: int done = FALSE, index;

18: char buffer[BUFLEN]; 19:

20: for (index = 0; index < SIZE; index++) 21: ref(index) = index; // Присваивание ссылочной функции!

22: while (!done) {

23: ShowArray();

24: cout « "\nEnter index from 0 to 9, Enter to quit:";

25: cin.getline(buffer, BUFLEN);

26: done = (strlen(buffer) == 0);

27: if (!done) { 28: index = atoi(buffer);

29: cout << "Enter floating-point value:";

30: cin.getline(buffer, BUFLEN); 31: ref(index) = atof(buffer); 32: } 33: }

34: return 0;

35: }

37: double &ref(int index)

38: {

39: if ((index < 0) || (index >= SIZE))

40: index = 0;

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]