Программирование на C и C++

Онлайн справочник программиста на C и C++

Дружественные функции

Функция, не являющаяся членом класса, может иметь доступ к его частным членам в случае, если она объявлена другом (friend) класса. Например, в следующем примере функция frd() объявлена другом класса cl:

class cl {
...
public:
friend void frd();
...
};

Как можно видеть, ключевое слово friend предшествует объявлению функции.

Одна из причин, почему язык С++ допускает существование функций-друзей, связана с той ситуацией, когда два класса должны использовать одну и ту же функцию. В качестве примера рассмотрим программу, в которой определяются два класса — 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 I, box b);
void set_color(int c);
void define_line (int x, int y, int I);
void show_line();
};

Функция same_color() не является членом ни одного из классов, но является другом обоих. Она возвращает истину, если объект типа line и объект типа box нарисованы одним и тем же цветом, и значение 0 — в противном случае. Следующий код определяет функцию same_color():

// возвращает истину, если линия и прямоугольник имеют одинаковый цвет
int same_color (line I, 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. В языке С++ предварительная ссылка на класс состоит из ключево­го слова class, после которого следует имя класса.

Следующая программа демонстрирует классы line и box и показывает, как дружественная фун­кция может получать доступ к частным членам класса.

#include <iostream.h>
#include <conio.h>
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 I, box b);
void set_color(int c);
void define_line (int x, int y, int I);
void show_line();
};
// возвращает истину, если линия и прямоугольник имеют одинаковый цвет
int same_color (line I, box b)
{
if (l.color == b.color) return 1;
return 0;
}
void box::set_color(int c)
{
color = c;
}
void line::set_color(int c)
{
color = c;
}
void box::define_box (int x1, int y1, int x2, int y2)
{
upx = x1;
upy = y1;
lowx = x2;
lowy = y2;
}
void box::show_box()
{
int i;
textcolor(color);
gotoxy(upx, upy);
for (i=upx; i<=lowx; i++) cprintf("-");
gotoxy(upx, lowy-1);
for (i=upx; i<=lowx; i++) cprintf("-");
gotoxy(upx, upy);
for (i=upy; i<=lowy; i++) {
cprintf (" | ");
gotoxy(upx , i);
}
gotoxy(lowx, upy);
for (i=upy; i<=lowy; i++) {
cprintf ("I");
gotoxy(lowx, i);
}
}
void line::define_line (int x, int y, int I)
{
startx = x;
starty = y;
len = I;
}
void line::show_line()
{
int i;
textcolor(color);
gotoxy(startx, starty);
for (i=0; i<len; i++) cprintf("-");
}
int main()
{
box b;
line I;
b.define_box (10, 10, 15, 15);
b.set_color(3);
b.show_box();
l.define_line (2, 2, 10);
l.set_color(2);
l.show_line();
if ( !same_color (I, b) ) cout << "Not the same.\n";
cout << "\nPress a key.";
getch ();
// теперь делаем линию и прямоугольник одного цвета
l.define_line(2, 2, 10);
l.set_color (3);
I.show_line();
if (same_color (l, b) ) cout << "Are the same color.\n";
return 0;
}

Имеется два важных ограничения применительно к дружественным функциям. Первое заключается в том, что производные классы не наследуют дружественных функций. Второе заключается в том, что дружественные функции не могут объявляться с ключевыми словами static или extern.