- •Чистые виртуальные функции и абстрактные классы
- •Производные классы с конструкторами и деструкторами
- •Виртуальные деструкторы
- •Обобщенный пример наследования
- •Модульность классов
- •Расширяющяся иерархия классов
- •Узловые классы
- •Множественное наследование классов
- •Шаблоны
- •Шаблоны функций
- •Перегрузка шаблонов функций
- •Шаблоны классов
- •Друзья Дружественные функции
- •Дружественные классы
- •Статические элементы класса
Друзья Дружественные функции
Одним из важных принципов С++ является защита данных от несанкционированного использования с помощью режимов доступа к элементам класса. Обычно доступ к собственным (private) элементам класса ограничивается методами этого класса. Однако возникают ситуации, когда необходимо, чтобы к закрытым элементам данных класса имела доступ функция, не принадлежащая этому классу и даже из другого класса. Это можно сделать, объявив эту функцию с помощью ключевого слова friend (друг) как дружественную функцию (friend function). Например,
class X
{ int n;
public:
friend void fr (void);
};
Функция fr может обращаться к элементу n.
Обычное объявление функции-элемента гарантирует три логически разные вещи:
функция имеет право доступа к закрытой части объявления класса;
функция находится в области видимости класса;
функция должна вызываться для объекта класса (имеется указатель this).
Объявив функцию как friend, мы наделяем ее только первым свойством. Причиной введения функций-друзей явилась ситуация, когда одна и та же функция должна использовать закрытые элементы двух или более классов. Объявление друзей не является (в общем случае) нарушением принципа инкапсуляции данных, поскольку сам класс разрешает доступ функции-другу к закрытой части класса. Это можно рассматривать как модель отношения людей, когда наши близкие друзья могут приходить к нам в дом, в отличие от чужих людей.
Объявление friend можно поместить и в закрытой и в открытой части объявления класса. Также как и функции-элементы, функции-друзья явно указываются в объявлении класса, друзъями которого они являются. Поэтому они в той же мере являются частью интерфейса класса, как и функции-элементы.
Пример 50.
Представим программу использования функции-друга для двух классов (Box, Line). Она должна изобразить на экране цветные прямоугольники и линии, сравнивая попарно их цвета (color) функцией-другом samecolor(). Если цвета совпадают, выдается сообщение Same color, иначе Different color.
#include<iostream.h>
#include<conio.h>
class Line; // предварительное объявление класса Line
class Box // класс "прямоугольник"
{ int color; // цвет рамки
int upx, upy; // координаты левого верхнего угла
int lowx, lowy; // координаты правого нижнего угла
public:
friend int samecolor (Line l, Box b); // функция-друг
void setcolor (int c); // цвет
void definebox (int x1, int y1, int x2, int y2); // координаты
void showbox (); // вывод на экран
};
class Line // класс "линия"
{ int color; // цвет линии
int x0, y0; // координаты начала линии
int len; // длина линии
public: // прототипы функций:
friend int samecolor (Line l, Box b); // функция-друг
void setcolor (int c); // цвет рамки
void defineline (int x, int y, int l); // координаты и длина линии
void showline (); // вывод линии
};
// Описание методов класса Box:
void Box :: setcolor (int c) // установка цвета
{ color = c;
}
void Box :: definebox (int x1, int y1, int x2, int y2) // координаты
{ upx = x1; upy = y1; lowx = x2; lowy = y2;
}
void Box :: showbox () // метод вывода прямоугольника
{ window(upx, upy,lowx, lowy); // координаты окна
textbackground (color); // установка цвета фона
clrscr(); // чистка окна
textbackground (BLACK); // цвет фона
window (1, 1, 80, 25); // окно экрана
}
// Описание методов класса Line:
void Line :: setcolor (int c) // установка цвета линии
{ color = c;
}
void Line :: defineline (int x, int y, int l) // определение линии
{ x0 = x; y0 = y; len = l; // координаты и длина линии
}
void Line :: showline () // метод вывода линии
{ textcolor (color); // цвет линии
gotoxy (x0, y0); // начало линии
for (int k=0; k<len; k++) // цикл вывода линии
cprintf("%c", '-');
textcolor (WHITE); // смена цвета линии
}
// Описание функции-друга:
int samecolor (Line l, Box b)
{ if (l.color == b.cololr) // если цвета линии и окна одинаковы, то
return 1; // возврат 1, иначе
return 0; // возврат 0
}
void main () // главная функция
{ Line l; // создание объекта линии
Box b; // создание объекта прямоугольника
textbackground (BLACK); // установка цвета фона
clrscr (); // чистка экрана
b.definebox (5, 5, 25, 10); // задание координат прямоугольника
b.setcolor (RED); // цвет прямоугольника
l.defineline (5, 15, 30); // задание координат и длины линии
l.setcolor (BLUE); // цвет линии
b.showbox (); // вывод прямоугольника
l.showline (); // вывод линии
gotoxy (1,1); // координаты курсора
if (samecolor (l, b)) // если цвета линии и окна одинаковы
cputs ("Same colors\n"); // сообщение:"Цвета одинаковы"
else cputs ("Different colors\n"); // иначе "Цвета различны"
getch (); // задержка экрана
}
Комментарии к программе.
В программе дано предварительное объявление класса Line, похожее на прототип (class Line;), которое используется в классе Box, до того как определен класс Line, поэтому необходимо сделать такое уведомление.
Дружественной может быть не только внешняя функция, как в примере, но и метод другого класса. Например:
class X { // …
int* next();
};
class Y
{ friend int* X:: next();
// …
};
В примере, рассмотренном выше, можно было бы объявить функцию samecolor методом класса Box, изменив его описание:
class Box
{ ...
public:
int samecolor ( Line l); // метод класса Box
...
};
В классе Line необходимо объявить функцию-друга:
class Line
{ ...
public:
friend int Box::samecolor (Line l); // функция-друг
...
};
В классе Line использовано полное имя функции Box::samecolor(). Кроме того, можно не указывать в качестве аргумента объект класса Box. Новая функция-друг Box::samecolor() примет вид:
int Box::samecolor ( Line l )
{ if (l.color == color) // используется указатель this -> color
return 1;
return 0;
}
