
- •1.2 Философские замечания
- •1.3 Процедурное программирование
- •1.4 Модульное программирование
- •1.5 Абстракция данных
- •1.6 Пределы абстракции данных
- •1.7 Объектно-ориентированное программирование
- •1.8 Концепции объектно-ориентированного программирования
- •1.8.1 Инкапсуляция
- •1.8.2 Полиморфизм
- •1.8.3 Наследование
- •1.10 Несколько полезных советов
- •2.2 Перегрузка функций
- •2.3 Перегрузка операторов
- •2.4 Наследование
- •2.5 Конструкторы и деструкторы
- •2.7 Два новых типа данных
- •Глава 3. Классы и объекты
- •3.1 Параметризованные конструкторы
- •3.2 Дружественные функции
- •3.3 Значения аргументов функции по умолчанию
- •3.3.1 Корректное использование аргументов по умолчанию
- •3.4 Взаимосвязь классов и структур
- •3.5 Связь объединений и классов
- •3.6 Анонимные объединения
- •3.7 Inline-функции
- •3.7.1 Создание inline-функций внутри класса
- •3.8 Передача объектов в функции
- •3.9 Возвращение объектов функциями
- •3.10 Присваивание объектов
- •3.11 Конструктор копирования
- •3.12 Массивы объектов
- •3.12.1 Инициализация массивов объектов
- •3.12.2 Создание инициализированных и неинициализированных массивов
- •3.13 Указатели на объекты
- •3.14 Статические члены класса
- •Глава 4. Перегрузка функций и операторов
- •4.1 Перегрузка конструкторов
- •4.2 Локализация переменных
- •4.3 Локализация создания объектов
- •4.4 Перегрузка функций и неопределенность
- •4.5 Определение адреса перегруженной функции
- •4.6 Указатель this
- •4.7 Перегрузка операторов
- •4.8 Дружественная функция-оператор
- •4.9 Ссылки
- •4.9.1 Параметры-ссылки
- •4.9.2 Передача ссылок на объекты
- •4.9.3 Возврат ссылок
- •4.9.4 Независимые ссылки
- •4.9.5 Использование ссылок для перегрузки унарных операторов
- •4.10 Перегрузка оператора []
- •4.11 Создание функций преобразования типов
- •Глава 5. Наследование, виртуальные функции и полиморфизм
- •5.1 Наследование и спецификаторы доступа
- •5.1.1 Спецификаторы доступа
- •5.1.2 Спецификатор доступа при наследовании базового класса
- •5.1.3 Дополнительная спецификация доступа при наследовании
- •5.2 Конструкторы и деструкторы производных классов
- •5.3 Множественное наследование
- •5.4 Передача параметров в базовый класс
- •5.5 Указатели и ссылки на производные типы
- •5.6 Ссылки на производные классы
- •5.7 Виртуальные функции
- •5.8 Для чего нужны виртуальные функции?
- •5.9 Чисто виртуальные функции и абстрактные типы
- •5.10 Виртуальный базовый класс
- •5.11 Раннее и позднее связывание
- •Глава 6. Подсистема динамического выделения памяти
- •6.1 Введение в обработку исключений
- •6.1.1 Перехват всех исключений
- •6.2 Работа с памятью с помощью new и delete
- •6.3 Размещение объектов
- •6.4 Перегрузка new u delete
- •7.1.1 Потоки
- •7.3 Создание собственных операторов вставки и извлечения
- •7.3.1 Создание операторов вставки
- •7.3.2 Перегрузка операторов извлечения
- •7.4 Форматирование ввода/вывода
- •7.4.1 Форматирование с помощью функций-членов класса ios
- •7.4.2 Использование манипуляторов
- •7.5 Создание собственных функций-манипуляторов
- •7.5.1 Создание манипуляторов без параметров
- •7.5.2 Создание манипуляторов с параметрами
- •7.6 Файловый ввод/вывод
- •7.6.1 Открытие и закрытие файлов
- •7.6.2 Чтение и запись в текстовые файлы
- •7.6.3 Двоичный ввод/вывод
- •7.6.4 Определение конца файла
- •7.6.5 Произвольный доступ
- •Глава 8. Ввод/вывод в массивы
- •8.1 Классы ввода/вывода в массивы
- •8.2 Создание потока вывода
- •8.3 Ввод из массива
- •8.4 Использование функций-членов класса ios
- •8.5 Потоки ввода/вывода в массивы
- •8.6 Произвольный доступ в массив
- •8.7 Использование динамических массивов
- •8.8 Манипуляторы и ввод/вывод в массив
- •8.9 Собственные операторы извлечения и вставки
- •8.10 Форматирование на основе массивов
- •Глава 9. Шаблоны и библиотека stl
- •9.1 Функции-шаблоны
- •9.2 Функции с двумя типами-шаблонами
- •9.3 Ограничения на функции-шаблоны
- •9.4 Классы-шаблоны
- •9.5 Пример с двумя типами-шаблонами
- •9.6 Обзор библиотеки stl
- •9.7 Класс vector
- •9.7 Класс string
- •9.8 Класс list
4.9 Ссылки
Ссылка (reference) по существу является неявным указателем. Она используется как другое имя объекта.
4.9.1 Параметры-ссылки
Если в языке C требовалось предоставить функции возможность изменять значения своих переменных-аргументов, приходилось явным образом объявлять параметры как указатели. Тогда функция могла обрабатывать значения переданных переменных, используя оператор *. Например, следующая программа реализует функцию swap(), обменивающую между собой значения двух аргументов целого типа:
#include <iostream.h>
void swap(int *a, int *b)
{
int t;
t = *a; *a = *b; *b = t;
}
int main()
{
int x=99, y=88;
cout << x << " " << у << "\n";
swap(&x, &y); // обмен значений
cout << x << " " << у << "\n";
return 0;
}
При вызове функции swap() перед подставляемыми в нее переменными должен стоять оператор взятия адреса &. Именно так реализуется передача по адресу в С. Хотя C++ позволяет подобный синтаксис, он поддерживает и более ясный, более прозрачный метод передачи по ссылке, используя параметр типа ссылки (reference parameter). В таком случае при объявлении функции перед параметром ставится знак амперсанта &. В качестве примера рассмотрим функцию:
void f(int &i)
{
i = rand(); // изменяет переданный аргумент
}
Обратим внимание, что инструкция
i = rand();
не использует оператор *. При объявлении параметра-ссылки компилятор C++ знает, что это неявный указатель, и обрабатывает его соответствующим образом. Всякий раз при вызове функции f() ей автоматически передается адрес аргументов. Например, в следующем фрагменте кода
int i;
f(i); // i получает случайное значение
printf("%d", i);
адрес переменной i, а не значение этой переменной, передается функции f(). Благодаря этому функция f() может модифицировать значение переменной i. Обратим внимание, что при вызове функции f() нет необходимости ставить оператор взятия адреса & перед переменной i. Компилятор автоматически передает функции адрес переменной i.
Для того чтобы проиллюстрировать использование параметров ссылочного типа, мы перепишем функцию swap() с использованием ссылок. Обратим внимание, каким образом функция swap() объявляется и вызывается:
#include <iostream.h>
void swap(int &a, int &b)
{
int t;
t = a; a = b; b = t; }
int main()
{
int x=99, у=88;
cout << х << " " << у << "\n";
swap(x, у); // обмен значений
cout << х << " " << у << "\n";
return 0;
}
Еще раз отметим, что определив а и b как параметры ссылочного типа, мы не нуждаемся в использовании оператора взятия адреса & при вызове функции swap(), а также не используем оператор * внутри функции swap(). На применение переменных ссылочного типа имеется ряд ограничений:
Нельзя получить адрес переменной ссылочного типа.
Нельзя создать массив ссылок.
Нельзя создать указатель на ссылку.
Ссылки на битовые поля не допускаются.
4.9.2 Передача ссылок на объекты
Как объяснялось в прошлой главе, когда объект передается функции как аргумент, создается копия объекта. Более того, при создании этой копии конструктор объекта не вызывается. Вместо этого делается точная копия объекта. Однако по окончании выполнения функции вызывается деструктор копии объекта. Если по каким-либо причинам нежелательно, чтобы создавалась копия объекта или чтобы вызывался деструктор, то следует передать объект в функцию по ссылке. При передаче по ссылке копия объекта не создается. Это также означает, что объект-параметр не будет уничтожаться при выходе из функции и соответственно не будет вызван деструктор. В качестве примера рассмотрим следующую программу:
#include <iostream.h>
class C
{
int id;
public:
int i;
C(int i) { cout << "Constructing " << i << "\n"; id = i; }
~C() { cout << "Destructing " << id << "\n"; }
void neg(C &p) { p.i = -p.i;}
};
int main()
{
C A(1);
A.i = 10;
A.neg(A);
cout << A.i << "\n";
return 0;
}
Программа выдает следующие результаты:
Constructing 1
-10
Destructing 1
Как можно видеть, деструктор объекта A вызывался только один раз.
При передаче объекта в функцию по ссылке действие над объектом внутри функции модифицирует этот объект. И эти изменения сохраняют силу после завершения работы функции.