В качестве примера дружественных классов рассмотрим класс Window_mgr (см. раздел 7.3.1), его членам понадобится доступ к внутренним данным объектов класса Screen, которыми они управляют. Предположим, например, что в класс Window_mgr необходимо добавить функцию-член clear(), заполняющую содержимое определенного окна пробелами. Для этого функции clear() нужен доступ к закрытым переменным-членам класса Screen. Для этого класс Screen должен объявить класс Window_mgr дружественным:

class Screen {

 // члены класса Window_Mgr смогут обращаться к закрытым

 // членам класса Screen

 friend class Window_mgr;

 // ... остальное, как раньше в классе Screen

};

Функции-члены дружественного класса могут обращаться ко всем членам класса, объявившего его другом, включая не открытые члены. Теперь, когда класс Window_mgr является другом класса Screen, функцию-член clear() класса Window_mgr можно переписать следующим образом:

class Window_mgr {

public:

 // идентификатор области для каждого окна на экране

 using ScreenIndex = std::vector::size_type;

 // сбросить данное окно, заполнив его пробелами

 void clear(ScreenIndex);

private:

 std::vector screens{Screen(24, 80, ' ')};

};

void Window_mgr::clear(ScreenIndex i) {

 // s - ссылка на окно, которое предстоит очистить

 Screen &s = screens[i];

 // сбросить данное окно, заполнив его пробелами

 s.contents = string(s.height * s.width, ' ');

}

Сначала определим s как ссылку на класс Screen в позиции i вектора окон. Затем переменные-члены height и width данного объекта класса Screen используются для вычисления количества символов новой строки, содержащей пробелы. Эта заполненная пробелами строка присваивается переменной-члену contents.

Если бы функция clear() не была дружественной классу Screen, то этот код не компилировался бы. Функция clear() не смогла бы использовать переменные-члены height, width или contents класса Screen. Поскольку класс Screen установил дружественные отношения с классом Window_mgr, для его функций доступны все члены класса Screen.

Важно понимать, что дружественные отношения не передаются. Таким образом, если у класса Window_mgr есть собственные друзья, то у них нет привилегий доступа к членам класса Screen.

Каждый класс сам контролирует, какие классы или функции будут его друзьями.

Как сделать функцию-член дружественной

Вместо того чтобы делать весь класс Window_mgr дружественным классу Screen, можно предоставить доступ только функции-члену clear(). Когда функция-член объявляется дружественной, следует указать класс, которому она принадлежит:

class Screen {

 // класс Window_mgr::clear должен быть объявлен перед классом Screen

 friend void Window_mgr::clear(ScreenIndex);

 // ... остальное как раньше в классе Screen

};

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

• Сначала определите класс Window_mgr, который объявляет, но не может определить функцию clear(). Класс Screen должен быть объявлен до того, как функция clear() сможет использовать члены класса Screen.

• Затем определите класс Screen, включая объявление функции clear() дружественной.

• И наконец, определите функцию clear(), способную теперь обращаться к членам класса Screen.

Перегруженные функции и дружественные отношения

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

// перегруженные функции storeOn

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

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

class Screen {

 // версия ostream функции storeOn может обращаться к закрытым членам

 // объектов класса Screen

Перейти на страницу:

Похожие книги