Объектно-ориентированное программирование на C++

Доступ к защищенным элементам класса


Как вы, наверное, помните, одно из основных требований для написания хороших программ с использованием объектно-ориентированного подхода состоит в том, чтобы максимально исключить возможность манипулирование элементами данными напрямую. Вместо этого должен быть представлен достаточно широкий набор методов, через которые должен осуществляться доступ к данным для их наблюдения, модификации и др.

Однако иногда использование методов для доступа к элементам данным может оказаться неудобным и неэффективным (поскольку на вызов метода требуется некоторое время). При этом речь может идти о доступе к элементам данным различных классов.

Например, рассмотрим новую функцию для нашего старого примера. Ее суть состоит в том, чтобы определить, лежит ли некоторый объект типа 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;

}


Содержание раздела