
- •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.3 Возврат ссылок
Функция может возвращать ссылку. В результате такая функция может использоваться в левой части оператора присваивания! В качестве примера рассмотрим следующую простую программу:
#include <iostream.h>
char& replace(int i, char *s) // возврат ссылки
{
return s[i];
}
char s[80] = "Hello There";
int main ()
{
replace(5) = 'X'; // присвоение Х пробелу после Hello
cout << s;
return 0;
}
Эта программа заменяет пробел между словами “Hello” и “There” символом ‘Х’. В результате программа выводит на экран «HelloXThere».
Функция replace() в соответствии со своим объявлением возвращает ссылку на символ. В соответствии со своей реализацией функция replace() возвращает ссылку на элемент массива s, определяющийся индексом i. Далее возвращаемая функцией replace() ссылка используется функцией main() для присвоения элементу буквы «X».
4.9.4 Независимые ссылки
Хотя в первую очередь ссылки включены в C++ для поддержки передачи параметров по ссылке и возвращения результата функции по ссылке, можно объявлять переменные ссылочного типа и как самостоятельные переменные. В таком случае они называются независимыми ссылками (independent references). Однако в таком случае их возможности серьезно ограничиваются.
Переменные ссылочного типа всегда должны указывать на какой-либо объект, поэтому они должны быть инициализированы при своем объявлении. В общем случае это означает присвоение им адреса уже объявленного объекта. После этого переменная ссылочного типа может использоваться всюду, где используется переменная, на которую она ссылается. Фактически между обеими переменными нет разницы. В качестве примера рассмотрим программу:
#include <iostream.h>
int main()
{
int j, k;
int &i = j; // независимая ссылка на j
j = 10;
cout << j << " " << i; // выводит 10 10
k = 121;
i = k; // копирование значения, а не адреса к в j
cout << "\n" << j; // выводит 121
return 0;
}
Программа выдаст следующие данные:
10 10
121
Адрес, на который указывает ссылочная переменная i, фиксирован и не может меняться. Так при выполнении присвоения i = k величина к копируется в j.
Вот другой пример. Инструкция i++ не вызывает изменения адреса. Вместо этого значение переменной k увеличивается на 1. (Надо помнить, что ссылки не являются указателями.)
Ссылочные переменные могут указывать на константы:
const int &i = 100;
4.9.5 Использование ссылок для перегрузки унарных операторов
Используя знания относительно ссылочных переменных, теперь можно легко понять, как унарные операторы перегружаются с помощью дружественных функций. Для начала вернемся к исходной версии оператора ++ по отношению к классу point. Для удобства он представлен ниже:
point point::operator++()
{
х++; У++; z++;
return *this;
}
Как известно, всякая функция-член имеет неявный аргумент, соответствующий ключевому слову this и являющийся указателем на объект, членом которого служит данная функция. По этой причине при перегрузке унарного оператора с помощью функции-члена нет необходимости для этой функции иметь какие-либо аргументы. Единственным необходимым аргументом в такой ситуации является указатель на объект, осуществляющий вызов функции-оператора. Поскольку this является указателем на объект, то любые изменения данных объекта влияют на объект, вызывающий функцию-оператор. В отличие от этого функции-друзья не получают указателя this и поэтому не могут ссылаться на объект, вызывающий их.
Для того чтобы выполнить перегрузку унарных операторов ++ или -- с помощью функции-друга, необходимо использовать параметр ссылочного типа. Ниже представлен вариант перегрузки оператора ++ с помощью дружественной функции:
class point
{
int x, y, z; // трехмерные координаты
public:
friend point operator+(point p1, point p2);
point operator=(point p2); // p1 подразумевается
friend point operator++(point &p1);
void show();
void set(int mx, int my, int mz);
};
point operator++(point &p1)
{
p1.x++; p1.y++; p1.z++;
return p1;
}
ПАМЯТКА: Для перегрузки операторов следует пользоваться функциями-членами. Функции-друзья предназначаются в C++ большей частью для специальных ситуаций.