- •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
3.2 Дружественные функции
Функция, не являющаяся членом класса, может иметь доступ к его частным членам в случае, если она объявлена другом (friend) класса. Например, в следующем примере функция F() объявлена другом класса C:
class C
{
public:
friend void F();
}
Как можно видеть, ключевое слово friend предшествует объявлению функции.
Одна из причин, почему язык C++ допускает существование функций-друзей, связана с той ситуацией, когда два класса должны использовать одну и ту же функцию. В качестве примера рассмотрим программу, в которой определяются два класса — line и box. Класс line содержит все необходимые данные и код для начертания горизонтальной пунктирной линии любой заданной длины, начиная с указанной точки с координатами (х, у) и с использованием заданного цвета. Класс box содержит необходимый код и данные для того, чтобы изобразить прямоугольник с заданными левой верхней и правой нижней точками указанным цветом. Оба класса используют функцию same_color() для того, чтобы определить, нарисованы ли линия и прямоугольник одним и тем же цветом. Объявление этих классов выполнено в следующем фрагменте программы:
class line;
class box
{
int color; // цвет прямоугольника
int upx, upy; // левый верхний угол
int lowx, lowy; // правый нижний угол
public:
friend int same_color(line l, box b);
void set_color(int c);
void define_box(int x1, int y1, int x2, int y2);
void show_box();
};
class line
{
int color;
int startx, starty;
int len;
public:
friend int same_color(line l, box b);
void set_color(int c);
void define_line(int x, int y, int l);
void show_line();
}
Функция same_color() не является членом ни одного из классов, но является другом обоих. Она возвращает истину, если объект типа line и объект типа box нарисованы одним и тем же цветом, и значение 0 — в противном случае. Следующий код определяет функцию same_color():
int same_color(line l, box b)
{
if(l.color == b.color) return 1;
return 0;
}
Как можно видеть, функция same_color() должна иметь доступ к частным частям обоих классов line и box. Поскольку она является другом каждого из классов, то она имеет право такого доступа. Более того, обратим внимание, что поскольку функция same_color() не является членом, то для ее использования нет необходимости в операторе области видимости или в использовании имени класса. Обратим внимание, что для тех же целей можно было создать функцию-член со спецификатором доступа public, которая возвращала бы цвета объектов типа line и box, а также еще одну функцию для сравнения этих цветов. Однако такой подход потребовал бы дополнительных вызовов функций, что в некоторых случаях неэффективно.
Обратим внимание на пустое объявление класса line, предшествующее объявлениям обоих классов box и line. Поскольку функция same_color() в классе box ссылается на объект типа line до того, как класс line будет определен, то необходима предварительная (forward) ссылка на класс line. Если этого не будет сделано, то компилятор не поймет, что такое line, когда будет анализировать объявление класса box. В языке C++ предварительная ссылка на класс состоит из ключевого слова class, после которого следует имя класса.
Следующий пример показывает, как дружественная функция может получать доступ к частным членам класса.
void main()
{
line l;
box b;
l.define_line(2, 2, 10); l.set_color(2);
b.define_box(2, 2, 10, 10); b.set_color(2);
if(same_color(l, b)) cout << "Are the same color.\n";
}
Имеется два важных ограничения применительно к дружественным функциям. Первое заключается в том, что производные классы не наследуют дружественных функций. Второе заключается в том, что дружественные функции не могут объявляться с ключевыми словами static или extern.
