Доступ к защищенным элементам класса
Как вы, наверное, помните, одно из основных требований для написания хороших программ с использованием объектно-ориентированного подхода состоит в том, чтобы максимально исключить возможность манипулирование элементами данными напрямую. Вместо этого должен быть представлен достаточно широкий набор методов, через которые должен осуществляться доступ к данным для их наблюдения, модификации и др.
Однако иногда использование методов для доступа к элементам данным может оказаться неудобным и неэффективным (поскольку на вызов метода требуется некоторое время). При этом речь может идти о доступе к элементам данным различных классов.
Например, рассмотрим новую функцию для нашего старого примера. Ее суть состоит в том, чтобы определить, лежит ли некоторый объект типа Point внутри области, занимаемой некоторым объектом типа Circle. Иными словами, нужно определить находится ли точка внутри окружности.
Для решения этой задачи нужны:
- координаты точки,
- координаты центра,
- радиус окружности.
Как видите все необходимые для этого данные лежат в областях private и protected.
Можно, конечно, написать новый метод для класса Circle, типа
Boolean Circle::IsInside(Point &P)
{
if ((X-P.GetX())*(X-P.GetX())+(Y-P.GetY())*(Y-P.GetY())<= R*R) return true;
else return false;
}
Но можно реализовать такую процедуру по-другому. И тому можно найти оправдание. Во-первых, вызов методов GetX() и GetY() для точки P можно здесь считать не очень элегантным, в чем-то даже громоздким. Было бы эффективнее непосредственно обращаться к X и Y. Однако если вы помните X и Y объявлены в разделе protected. Во вторых, методы определяются как процедуры для наблюдения и модификации элементов данных и вроде как, написание метода IsInside() не совсем оправдано, поскольку он не отвечает требованиям, предъявляемым к методам.
Тогда как же можно реализовать функцию, не являющуюся методом класса, но в тоже время, имеющую доступ к необщим членам класса.
Для того чтобы класс мог предоставлять функциям-неэлементам доступ к своей приватной части используется механизм объявления, так называемых, функций-друзей (friend) класса или дружественных функций. Для этого в декларации класса помещается декларация функции, перед которой стоит ключевое слово friend.
Один особенно полезный аспект механизма friend состоит в том, что функция может быть другом двух и более классов.
Посмотрим, как можно использовать механизм дружественных функций в нашем примере.
Для начала дополним декларацию классов Point и Circle:
class Point
{
//...
friend Boolean IsInside (Circle &C, Point &P);
};
class Circle: public Point
{
//...
friend Boolean IsInside (Circle &C, Point &P);
};
Заметьте, что объявление дружественной функции
friend Boolean IsInside (Circle &C, Point &P);
можно с одинаковым эффектом вставлять в любом месте в декларации класса, это может быть любой раздел (public, protected или даже private).
Сама функция будет:
Boolean IsInside(Circle &C, Point &P)
{
if ((C.X-P.X)*(C.X-P.X)+(C.Y-P.Y)*(C.Y-P.Y)<= C.R * C.R) return true;
else return false;
}
Таким образом, функция IsInside(), обычная функция (обычная в том смысле, что не является функцией элементом какого-то класса) получает доступ к приватной и защищенной областям классов Point и Circle. Обеспечить возможность такого доступа можно только в декларации класса. Так что в этом смысле инкапсуляция и защищенность данных сохраняется, поскольку полностью исключается всякая возможность такого доступа так, чтобы класс об это "не знал".
Теперь несколько слов о вариантах проявления механизма дружественных функций
Функция-элемент одного класса может быть дружественной иному классу. Например
class x
{
//...
void f();
};
class y
{
//...
friend void x::f();
};
Не исключен и такой вариант, когда все функции одного класса - дружественны другому классу. Для такого случая есть даже сокращенная запись
class y
{
//...
friend class x;
}