- •Введение. Принципы объектно-ориентированного программирования
- •Глава 1. Классы и объекты
- •1.1. Операция разрешения области видимости ::
- •1.2. Перечислимый тип
- •1.3. Модификатор const
- •1.4. Новый тип данных – ссылка &
- •Inline определение_функции
- •2. Определение класса. Сокрытие информации.
- •3. Объект.
- •4. Конструкторы и деструкторы
- •4.1.Назначение конструктора
- •4.2. Конструктор копирования
- •X::X(X&); // где X – имя класса
- •4.3. Деструктор
- •5. Неявный указатель this
- •6. Перегрузка операций
- •7. Примеры перегрузки некоторых операций
- •7.1. Перегрузка операции [ ]
- •7.2. Перегрузка операции ()
- •7.6. Перегрузка операции (тип)
- •8. Дружественность
- •Istream
- •10. Массивы объектов.
- •11. Функции- и классы-шаблоны
- •11.1 Функции-шаблоны (родовые функции)
- •11.2 Классы-шаблоны
- •12. Член-данные класса – объекты другого класса: агрегированные классы.
- •Глава 2. Наследование. Полиморфизм
- •1. Базовый и порожденный классы
- •2. Конструкторы порожденного класса
- •3. Стандартные преобразования при наследовании
- •4. Множественное наследование. Виртуальный базовый класс
- •4.1. Прямые базовые классы
- •4.2. Виртуальный базовый класс
- •5. Полиморфизм, раннее и позднее связывание, виртуальные функции
- •5.1 Раннее (статическое) и позднее (динамическое) связывание
- •5.2. Определение виртуальной функции
- •5.3. Чистая виртуальная функция и абстрактный класс
- •5.4. Правила определения виртуальных функций
- •5.5. Механизм позднего связывания
- •6. Библиотека fstream – работа с файлами
- •Глава 3. Библиотека стандартных шаблонов (бсш). Контейнеры
- •1. Контейнер. Структура бсш.
- •2. Контейнер Vector – динамический массив
- •Контейнер list – список
- •4. Контейнер Set – множество
- •Содержание
- •Глава 1. Классы и объекты
- •Глава 2. Наследование. Полиморфизм
- •Глава 3. Библиотека стандартных шаблонов (бсш). Контейнеры
8. Дружественность
Пример. Пусть некоторая функция Show выводит строку в красивом виде – обрамленную «звездочками»:
void Show(String &s, int cf, int ct)
{int i, m = s.len;
for(i = 0; i <= m + 1; i++) cout << ‘*’; cout << endl;
cout << ‘*’ << s.line << ‘*’ << endl;
for(i = 0; i <= m + 1; i++) cout << ‘*’; cout << endl;
}
Очевидно, что задать такую функцию компилятор не позволит, так как будет нарушена инкапсуляция член-данных len и line. Если все-таки необходимо разрешить некоторой не член-функции (внешней функции) использовать член-данные из части private какого-либо класса, ее можно объявить дружественной этому классу.
class String
{friend void Show(String &, int, int); // в любом месте определения класса
char *line;
…
};
Вообще другом класса может быть:
внешняя по отношению к классу функция, как в нашем примере;
член-функция известного на данный момент другого класса.
Например, если член-функция f класса A использует член-данные класса B, то f надо объявить «другом» классу B.
class B
{friend тип_возвр_знач A:: f(аргументы); // сама f определяется в A
...
};
другой определенный (или объявленный) на данный момент класс.
class A { ...};
class B {friend class A;
....
};
Такое объявление означает, что всем член-функциям класса A разрешается доступ ко всем член-данным класса B, но не наоборот.
Замечание 1. Дружественность нужно использовать оптимально, так как она нарушает принцип инкапсуляции.
Замечание 2. Операции можно перегружать и как внешние дружественные классу функции. В этом случае одноместная операция имеет один аргумент – объект класса, а двуместная – два: объект класса и второй операнд.
Пример. Покажем, как будет выглядеть перегрузка операции + как внешней дружественной функции.
class String
{ ...
friend String operator + (String & s, String & t)
{String z(s.len + t.len + 1); // определим локальную переменную суммарной длины
strcpy(z.line, s.line); strcat(z.line, t.line);
z.len = strlen(z.line);
return z;
}
…
};
Используется она так же, как и перегруженная в классе.
9. Перегрузка операций потокового ввода >> и вывода <<.
Для использования операций потокового ввода и вывода надо подключить заголовочный файл <iostream.h>. Библиотека iostream содержит стандартные классы ввода-вывода:
класс istream – потоковый ввод со стандартного устройства stdin (клавиатура),
класс ostream – потоковый вывод на стандартное устройство вывода stdout(монитор). Рассмотрим их.
ostream
В классе ostream определена операция <<, перегруженная для форматизированного вывода базовых типов данных, т.е.
class ostream{ ...
public: ostream & operator << (char *);
ostream & operator << (char);
ostream & operator << (int);
ostream & operator << (long int);
ostream & operator << (double);
...
};
cout – это стандартное имя потока вывода, т.е. в системе есть описание
ostream cout;
Поэтому операцию cout << x рассматриваем как двуместную: слева первый операнд – имя потока вывода, справа второй операнд – имя переменной. Так как возвращаемое значение – ссылка на cout, то можно писать цепочки вывода.
Например, пусть задана переменная x
int x;
Цепочка вывода
cout << ”x = “ << x << ’\n’;
представляет собой последовательное выполнение операции << с аргументами разного типа:
char * int char
((cout.operator <<(“x =”)).operator <<(x)).operator<<(‘\n’);
cout
cout
cout
Оператор, определенный как член-функция класса, первым операндом всегда имеет объект класса, т.е. *this. Первым операндом операции << является поток вывода, поэтому ее можно перегрузить для абстрактных типов только, как дружественную классу.
Например, для класа String эта перегрузка может быть определена таким образом.
class String{
public:
...
friend ostream & operator << (ostream &r, String &s)
{ r << s.line;
return r;
}
};
Теперь и для объектов класса String можно применять операцию потокового вывода <<:
String s(«Иванов»);
cout << s;
