13. 15.2. Друзья

13. 15.2. Друзья

Рассмотрим еще раз перегруженные операторы равенства для класса String, определенные в области видимости пространства имен. Оператор равенства для двух объектов String выглядит следующим образом:

bool operator==( const String &str1, const String &str2 )

{

if ( str1.size() != str2.size() )

return false;

return strcmp( str1.c_str(), str2.c_str() ) ? false : true;

}

Сравните это определение с определением того же оператора как функции-члена:

bool String::operator==( const String &rhs ) const

{

if ( _size != rhs._size )

return false;

return strcmp( _string, rhs._string ) ? false : true;

}

Нам пришлось модифицировать способ обращения к закрытым членам класса String. Поскольку новый оператор равенства – это глобальная функция, а не функция-член, у него нет доступа к закрытым членам класса String. Для получения размера объекта String и лежащей в его основе C-строки символов используются функции-члены size() и c_str().

Альтернативной реализацией является объявление глобальных операторов равенства друзьями класса String. Если функция или оператор объявлены таким образом, им предоставляется доступ к неоткрытым членам.

Объявление друга (оно начинается с ключевого слова friend) встречается только внутри определения класса. Поскольку друзья не являются членами класса, объявляющего дружественные отношения, то безразлично, в какой из секций – public, private или protected – они объявлены. В примере ниже мы решили поместить все подобные объявления сразу после заголовка класса:

class String {

friend bool operator==( const String &, const String & );

friend bool operator==( const char *, const String & );

friend bool operator==( const String &, const char * );

public:

// ... остальная часть класса String

};

В этих трех строчках три перегруженных оператора сравнения, принадлежащие глобальной области видимости, объявляются друзьями класса String, а следовательно, в их определениях можно напрямую обращаться к закрытым членам данного класса:

// дружественные операторы напрямую обращаются к закрытым членам

// класса String

bool operator==( const String &str1, const String &str2 )

{

if ( str1._size != str2._size )

return false;

return strcmp( str1._string, str2._string ) ? false : true;

}

inline bool operator==( const String &str, const char *s )

{

return strcmp( str._string, s ) ? false : true;

}

// и т.д.

Можно возразить, что в данном случае прямой доступ к членам _size и _string необязателен, так как встроенные функции c_str() и size() столь же эффективны и при этом сохраняют инкапсуляцию, а значит, нет особой нужды объявлять операторы равенства для класса String его друзьями.

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

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

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

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

extern ostream& storeOn( ostream &, Screen & );

extern BitMap& storeOn( BitMap &, Screen & );

// ...

class Screen

{

friend ostream& storeOn( ostream &, Screen & );

friend BitMap& storeOn( BitMap &, Screen & );

// ...

};

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

Объявление функции другом двух классов должно выглядеть так:

class Window; // это всего лишь объявление

class Screen {

friend bool is_equal( Screen &, Window & );

// ...

};

class Window {

friend bool is_equal( Screen &, Window & );

// ...

};

Если же мы решили сделать функцию членом одного класса и другом второго, то объявления будут построены следующим образом:

class Window;

class Screen {

// copy() - член класса Screen

Screen& copy( Window & );

// ...

};

class Window {

// Screen::copy() - друг класса Window

friend Screen& Screen::copy( Window & );

// ...

};

Screen& Screen::copy( Window & ) { /* ... */ }

Функция-член одного класса не может быть объявлена другом второго, пока компилятор не увидел определения ее собственного класса. Это не всегда возможно. Предположим, что Screen должен объявить некоторые функции-члены Window своими друзьями, а Window – объявить таким же образом некоторые функции-члена Screen. В таком случае весь класс Window объявляется другом Screen:

class Window;

class Screen {

friend class Window;

// ...

};

К закрытым членам класса Screen теперь можно обращаться из любой функции-члена Window.

Упражнение 15.6

Реализуйте операторы ввода и вывода, определенные для класса Screen в упражнении 15.5, в виде друзей и модифицируйте их определения так, чтобы они напрямую обращались к закрытым членам. Какая реализация лучше? Объясните почему.

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг:

Совет 21: Опасные друзья

Из книги автора

Совет 21: Опасные друзья Если взрослый способен сам оградить себя от не очень умных людей из интер нета, то ребенок, увы, может быть не очень разборчивым. Общение с неизвестными пользователями может быть не просто неприятным, а очень опасным, ведь ребенку неоткуда знать, что


Кандинский и Воннегут теперь друзья

Из книги автора

Кандинский и Воннегут теперь друзья Зная, как легко в Сети формируются группы, просто ошибиться, приняв количество за качество. “Фейсбук” уже способствует процессам, которые не требуют социальных скреп. Истина в том, что людям свойственно собираться в группы. Социальным


Френды (друзья) в блогах. Образование сообществ

Из книги автора

Френды (друзья) в блогах. Образование сообществ Как уже неоднократно было сказано, особенность Web 2.0 – его интерактивность. Создатели концепции Web 2.0 считают, что только сами люди могут определить, что на самом деле им надо, и самостоятельное решение этого вопроса –


Френды (друзья) в блогах

Из книги автора

Френды (друзья) в блогах Как уже неоднократно обсуждалось, особенность современной части Интернета, которую называют Web 2.0 – его интерактивность. Создатели концепции Web 2.0 считают, что только сами люди могут определить, что на самом деле им надо, и самостоятельное решение


Друзья

Из книги автора

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


Зачем компании нужны друзья на Facebook?

Из книги автора

Зачем компании нужны друзья на Facebook? Если вы знаете ответ на данный вопрос, то можете пропустить эту главу. Если сомневаетесь в ответе, давайте порассуждаем вместе. Я знаю как минимум пять причин:1. Поддержание отношений с имеющимися клиентами.2. Поиск новых клиентов через


Генераторы - лучшие друзья первичных ключей

Из книги автора

Генераторы - лучшие друзья первичных ключей Надо сказать несколько слов о реализации первичного ключа. Так как он предназначен для обеспечения уникальности, то никакие две записи в одной таблице не могут иметь одинаковых значений этого ключа. То есть, чтобы


R.11.4 Друзья

Из книги автора

R.11.4 Друзья Другом класса называется функция, которая не является членом класса, но в которой можно использовать частные и защищенные члены этого класса. Имя друга не принадлежит области видимости класса, и дружественная функция не вызывается с помощью операций доступа к


R.14.7 Друзья

Из книги автора

R.14.7 Друзья Функция-друг для шаблона типа не является неявной шаблонной функцией, например:template‹class T› class task { //… friend void next_time(); friend task‹T›* preempt(task‹T›*); friend task* prmt(task*); // ошибка //…};Здесь функция next_time() становится другом всех классов task, а каждый класс task имеет в качестве


13. 15.2. Друзья

Из книги автора

13. 15.2. Друзья Рассмотрим еще раз перегруженные операторы равенства для класса String, определенные в области видимости пространства имен. Оператор равенства для двух объектов String выглядит следующим образом:bool operator==( const String &str1, const String &str2 ){if ( str1.size() != str2.size() )return false;return


1.15 Друзья (friend)

Из книги автора

1.15 Друзья (friend) Функция operator+() не воздействует непосредственно на представление вектора. Действительно, она не может этого делать, поскольку не является членом. Однако иногда желательно дать функциям не членам возможность доступа к закрытой части класса. Например, если


5.4 Друзья и Объединения

Из книги автора

5.4 Друзья и Объединения В это разделе описываются еще некоторые особенности, ксающиеся классов. Показано, как предоставить функции не члену доступ к закрытым членам. Описывается, как разрешать конфлиты имен членов, как можно делать вложенные описания классов, и как


5.4 Друзья и Объединения

Из книги автора

5.4 Друзья и Объединения В это разделе описываются еще некоторые особенности, ксающиеся классов. Показано, как предоставить функции не члену доступ к закрытым членам. Описывается, как разрешать конфликты имен членов, как можно делать вложенные описания классов, и как


5.4.1 Друзья

Из книги автора

5.4.1 Друзья Предположим, вы определили два класса, vector и matrix (вектор и матрица). Каждый скрывает свое представление и прдоставляет полный набор действий для манипуляции объектами его типа. Теперь определим функцию, умножающую матрицу на вектор. Для простоты допустим, что в


6.10 Друзья и Члены

Из книги автора

6.10 Друзья и Члены Теперь, наконец, можно обсудить, в каких случаях для доступа к закрытой части определяемого пользователем типа ипользовать члены, а в каких – друзей. Некоторые операции должны быть членами: конструкторы, деструкторы и виртуальные функции (см. следующую


Celiac Plexus: друзья суда, дата-майнинг, новостной клиппинг и передовой окоп копирастии Сергей Голубицкий

Из книги автора

Celiac Plexus: друзья суда, дата-майнинг, новостной клиппинг и передовой окоп копирастии Сергей Голубицкий Опубликовано 04 марта 2013 Сегодня мы существенно углубим дискуссию об авторских правах (вернее — об их нарушении, мнимом и реальном), а также