Ключевое слово friend – спецификатор функции, который дает функции – не члену класса доступ к скрытым членам класса. Он используется для того, чтобы выйти за строгие рамки типизации и сокрытия данных в C++. Однако, должны быть весьма серьезные причины для выхода за пределы этих ограничений, поскольку от них зависит надежность программирования. Поэтому использование friend функций в языке C++ спорно.
Одна из причин их использования состоит в том, что некоторые функции нуждаются в привилегированном доступе более, чем к одному классу. Вторая причина – friend-функция передает все параметры через список параметров, и значение каждого из них подчинено преобразованию, совместимому с назначением. Такие преобразования применяются к явно переданным аргументам-классам и поэтому особенно полезны в случаях перегрузки оператора. Это будет показано в следующем разделе.
Объявление friend (дружественной) функции должно появляться внутри объявления класса, которому она дружественна. Имени функции предшествует ключевое слово friend, и ее объявление может находится как в public, так и в private части класса, что не повлияет на значение. Функция-член одного класса может быть friend-функцией другого класса. Это происходит тогда, когда функция-член объявлена в friend классе с использованием оператора разрешения контекста для определения имени функции дружественного класса. Если все функции-члены одного класса являются friend-функциями второго класса, то это можно определить записью
friend class имя класса.
Следующие объявления иллюстрируют синтаксис:
class tweedledee
{...
friend void alice(); //friend-функция
int Cheshire(); //функция-член
...
};
class tweedledum {
...
friend int tweedledee::Cheshire();
...
};
class tweedledumber
(...
friend class tweedledee; //все функции-члены
... //tweedledee имеют доступ
};
Рассмотрите класс matrix (см. упражнение 6.24) и класс vect (см. главу 6). Функция умножения вектора на матрицу, которые представлены этими двумя классами, может быть написана более эффективно, если будет иметь доступ к private членам обоих классов. Эта функция будет friend для обоих классов. В главе 6 безопасный доступ к элементам vect и matrix обеспечивался их соответствующими функциями-членами vect::element() и matrix::element(). Можно написать функцию, использующую этот тип доступа, которая будет умножать, не требуя статуса friend. Однако, необходимость в обращении к функциям и проверке выхода за пределы массива приведет к тому, что такая матрица будет умножаться исключительно неэффективно.
class matrix; //forward определение
class vect
{private:
int* p;
int size;
friend vect mpy(const vect& v, const matrix& m);
public:
...
};
class matrix
{ //хранит целые элементы
private:
int** base;
int row_size, column_size;
friend vect mpy(const vect& v, const matrix& m) ;
public:
...
};
vect mpy(const vect& v, const matrix& m)
{if (v.size != m.row_size) //неверные размеры
{cerr<<"multiply fai led - sizes incorrect "
<
Второстепенное значение требует предварительного описания класса matrix. Оно необходимо потому, что функция mpy должна появляться в обоих классах, и использует каждый класс как тип аргумента.
Friend-функции можно рассматривать как часть общего интерфейса класса. Существует ряд ситуаций, в которых они могут быть альтернативой функциям-членам. Использование friend спорно, потому что они наруша-ют инкапсуляцию, окружающую private члены классов. Парадигма ООП утверждает, что объскгы (в C++ они — переменные класса) доступны через их public члены. Только функции-члены должны иметь доступ к скрытой реа-лизации АТД. Это - ясный и строгий принцип проектирования. Friend-функция, однако, находится на самой его границе, поскольку имеет доступ к private членам, но сама при этом не является функцией-членом. С ее помощью можно реализовать быстрый код для доступа к подробностям реализации класса. Применяя такой механизм можно легко нарушить режим работы. Однако, как в предыдущем примере, где основной целью была эффективность, и в некоторых других ситуациях, friend-функции могут оказаться полезными.
ДРУЖЕСТВЕННЫЕ КЛАССЫ
Класс может определять объекты, необходимые в качестве внутренней подробности для других классов. Чтобы сохранять анонимность, такой класс должен быть частным. Класс, который обрабатывает эти частные объекты, должен быть с ними в отношениях дружественности.
Вспомните тип string, определенный с семантикой подсчета ссылок в главе 6. Индивидуальные значения строки, в конечном счете, ссылались на объекты типа str_obj. Это отделение позволяло одному экземпляру str_obj, требовавшему большого количества байт памяти, использоваться большим количеством экземпляров string. Мы можем преобразовать этот пример, используя отношение дружественности для сохранения секретности класса s tr_obj.
class str_obj
{private:
friend class string;
friend class string iterator;
friend ostream& operator«(ostream& out,const string& str);
int len, ref_cnt;
char* s;
str_obj ():len(0), ref_cnt(l) { s = new char[l]; }
//инициализаторы
str_obj(int n):len(n), ref_cnt(l) { s = new char[n + 1]; }
str obj(const char* p):ref cnt(l)
{ len = strlen(p); s = new char[len + 1];
strcpy(s, p); } ~str_obj () { delete [] s; }
};
Преимуществом этого проекта из двух классов будет увеличение гибкости дальнейшего отделения подробностей реализации от кода пользователя.
Похожие записи
No user прокомментировали сообщение
Оставить комментарий