Вы убедились, что передача объектов в функции как ссылок может быть более эффективной, чем передача их как значений. Передача объектов как ссылок позволяет вызываемой функции изменять значения переменных вызывающей функции.
Вы также узнали, что аргументы, передаваемые функции, и значения, возвращаемые из функций, могут передаваться как ссылки и этот процесс можно реализовать как с помощью указателей, так и с помощью ссылок.
Теперь вы научились для безопасной передачи значений между функциями использовать константные указатели на константные объекты или константные ссылки, благодаря чему достигается как эффективность, так и безопасность работы программы.
Вопросы и ответы
Зачем использовать ссыпки, если указатели могут делать ту же работу?
Ссылки легче использовать, и они проще для понимания. Косвенность обращений при этом скрывается, и отсутствует необходимость в многократном разыменовании переменных.
Зачем нужны указатели, если со ссыпками легче работать?
Ссылки не могут быть нулевыми, и их нельзя переназначать. Указатели предлагают большую гибкость, но их сложнее использовать.
Зачем вообще результат функции возвращать как значение?
Если возвращается объект, который является локальным в данной функции, необходимо организовать возврат его именно как значения, в противном случае возможно появление ссылки на несуществующий объект.
Если существует опасность от возвращения объекта как ссылки, почему бы тогда не сделать обязательным возврат по значению?
При возвращении объекта как ссылки достигается гораздо большая эффективность, которая заключается в экономии памяти и увеличении скорости работы программы.
Коллоквиум
В этом разделе предлагаются вопросы для самоконтроля и укрепления полученных знаний, а также ряд упражнений, которые помогут закрепить ваши практические навыки. Попытайтесь самостоятельно ответить на вопросы теста и выполнить задания, а потом сверьте полученные результаты с ответами в приложении Г. Не приступайте к изучению материала следующей главы, если для вас остались неясными хотя бы некоторые из предложенных ниже вопросов.
Контрольные вопросы
1. В чем разница между ссылкой и указателем?
2. Когда нужно использовать именно указатель, а не ссылку?
3. Что возвращает оператор new, если для создания нового объекта недостаточно памяги?
4. Что представляет собой константная ссылка?
5. В чем разница между передачей объекта как ссылки и передачей ссылки в функцию?
Упражнения
1. Напишите программу, которая объявляет переменную типа int, ссылку на значение типа int и указатель на значение типа int. Используйте указатель и ссылку для управления значением переменной типа int.
2. Напишите программу, которая объявляет константный указатель на постоянное целое значение. Инициализируйте этот указатель, чтобы он указывал на целочисленную переменную varOne. Присвойте переменной varOne значение 6. Используйте указатель, чтобы присвоить переменной varOne значение 7. Создайте вторую целочисленную переменную varTwo. Переназначьте указатель, чтобы он указывал на переменную varTwo. Пока не компилируйте это упражнение.
3. Скомпилируйте программу, написанную в упражнении 2. Какие действия компилятор считает ошибочными? Какие строки генерируют предупреждения?
4. Напишите программу, которая создает блуждающий указатель.
5. Исправьте программу из упражнения 4, чтобы блуждающий указатель стал нулевым.
6. Напишите программу, которая приводит к утечке памяти.
7. Исправьте программу из упражнения 6.
8. Жучки: что неправильно в этой программе?
1: #include <iostream.h>
2:
3: class CAT
4: {
5: public:
6: CAT(int age) { itsAge = age; }
7: ~CAT(){ }
8: int GetAge() const { return itsAge;}
9: private:
10: int itsAge;
11: };
12:
13: CAT & MakeCat(int age);
14: int main()
15: {
16: int age = 7;
17: CAT Boots = MakeCat(age);
18: cout << "Boots is " << Boots.GetAge() << " years old!n";
19: return 0;
20: }
21:
22: CAT & MakeCat(int age)
23: {
24: CAT * pCat = new CAT(age);
25: return *pCat;
26: }
9. Исправьте программу из упражнения 8.
День 10-й. Дополнительные возможности использования функции
На занятии 5 вы познакомились с основными принципами использования функций. Теперь, когда вы знаете, как работают указатели и ссылки, перед вами открываются дополнительные возможности. Сегодня вы узнаете:
• Как перегружать функции-члены
• Как перегружать операторы
• Как создавать функции для поддержания классов с динамическим выделением памяти для переменных
Перегруженные функции-члены
На занятии 5 вы получили общие представления о полиморфизме, или перегружаемости функций. Имеется в виду объявление двух или более функций под одним именем но с разными параметрами. Функции-члены класса можно перегружать точно так же.
В классе Rectangle (листинг 10.1) объявляются две функции DrawShape(). Первая, которая не содержит списка параметров, вычерчивает прямоугольник, основываясь на текущих значениях класса. Вторая принимает два значения — ширину и длину — и в соответствии с ними создает прямоугольник, игнорируя текущие значения класса.
Листинг 10.1. Перегрузка функций-членов
1: //Листинг 10.1. Перегрузка функций-членов
2: #include <iostream.h>
3:
4: int
5: // Обьявление класса Rectangle
6: class Rectangle
7: {
8: public:
9: // конструкторы
10: Rectangle(int width, int height);
11: ~Rectangle(){ }
12:
13: // перегрузка функции-члена класса DrawShape
14: void OrawShape() const;
15: void DrawShape(int aWidth, int aHeight) const;
16:
17: private:
18: int itsWidth;
19: int itsHeight;
20: };
21:
22: // Применение конструктора
23: Rectangle::Rectangle(int width, int height)
24: {
25: itsWidth = width;
26: itsHeight = height;
27: }
28:
29:
30: // Перегруженная функция DrawShape - вариант без передачи данных
31: // Создание прямоугольника по значениям, заданным по умолчанию
32: void Rectangle::DrawShape() const
33: {
34: DrawShape( itsWidth, itsHeight);
35: }
36:
37:
38: // Перегруженная функция DrawShape - передача двух значений
39: // Создание прямоугольника по значениям, переданным с параметрами
40: void Rectangle:;DrawShape(int width, int height) const
41: {
42: for (int i = 0; i<height; i++)
43: {
44: for (int j = 0; j< width; j++)
45: {
46: cout << "<<";
47: }
48: cout << "n";
49: }
50: }
51:
52: // Выполняемая программа, демонстрирующая использование перегруженных функций
53: int main()
54: {
55: // создание прямоугольника с размерами 30 и 5
56: Rectangle theRect(30,5);
57: cout << "DrawShape(): n";
58: theRect.DrawShape();
59: cout << "nDrawShape(40,2): n";
60: theRect.DrawShape(40,2);
61: return 0;
62: }
Результат:
DrawShape():
******************************
******************************
******************************
******************************
******************************
DrawShape(40,2):
****************************************
****************************************
Анализ: Листинг 10.1 представляет собой усеченную версию проекта, рассмотренного в главе подведения итогов за первую неделю. Чтобы сократить размер программы, был удален блок контроля за соответствием значений заданным типам. Основной код был упрощен до простой выполняемой программы без показа пользовательского меню.
Сейчас для нас важны строки 14 и 15, где происходит перегрузка функции DrawShape(). Использование перегруженных вариантов этой функции показано далее, в строках с 30 по 50. Обратите внимание, что версия функции DrawShape() без параметров обращается к варианту функции, содержащей два параметра, и передает в нее текущие значения переменных-членов. При программировании всегда следует избегать дублирования одинаковых программных кодов. В противном случае придется держать в памяти все созданные копии функций, чтобы при изменении программного кода в одной из них внести соответствующие изменения во все копии.
В строках программы с 52 по 62 создается прямоугольный объект и вызывается функция DrawShape(). В первый раз в функцию не передаются параметры, а во второй раз передается два значения типа unsigned short integer.
Компилятор выбирает правильное объявление функции по количеству и типу заданных параметров. Дополнительно можно задать в этой же программе еще одно объявление функции DrawShape(), в параметрах которой будет одно значение размера и переменная перечисления, позволяющая пользователю указать, что обозначает данный размер — ширину или длину прямоугольника.