friend std::ostream& storeOn(std::ostream &, Screen &); // ...
};
Класс Screen объявляет другом версию функции storeOn, получающей поток ostream&. Версия, получающая параметр BitMap&, особых прав доступа к объектам класса Screen не имеет.
Классы и функции, не являющиеся членами класса, не следует объявлять прежде, чем они будут использованы в объявлении дружественными. Когда имя впервые появляется в объявлении дружественной, оно неявно
Даже если мы определим функцию в классе, ее все равно придется объявить за пределами класса, чтобы сделать видимой. Объявление должно уже существовать, даже если вызывается дружественная функция:
struct X {
friend void f() { /*
X() { f(); } //
void g();
void h();
};
void X::g() { return f(); } //
void f(); //
void X::h() { return f(); } //
//
Важно понимать, что объявление дружественной затрагивает доступ, но не является объявлением в обычном смысле.
Упражнение 7.32. Определите собственные версии классов Screen и Window_mgr, в которых функция clear() является членом класса Window_mgr и другом класса Screen.
Каждый класс определяет собственную область видимости. Вне
Screen::pos ht = 24, wd = 80; //
//
Screen scr(ht, wd, ' ');
Screen *p = &scr
char c = scr.get(); //
scr c = p->get(); //
//
Тот факт, что класс определяет область видимости, объясняет, почему следует предоставить имя класса наравне с именем функции, при определении функции-члена вне ее класса (см. раздел 7.1.2). За пределами класса имена ее членов скрыты.
Как только имя класса становится видимо, остальная часть определения, включая список параметров и тело функции, находится в области видимости класса. В результате мы можем обращаться к другим членам класса без уточнения.
Вернемся, например, к функции-члену clear() класса Window_mgr (см. раздел 7.3.4). Параметр этой функции имеет тип, определенный в классе Window_mgr:
void Window_mgr::clear(ScreenIndex i) {
Screen &s = screens[i];
s.contents = string(s.height * s.width, ' ');
}
Поскольку компилятор видит последующий список параметров и ничего подобного в области видимости класса WindowMgr, нет никакой необходимости определять, что требуется тип ScreenIndex, определенный в классе WindowMgr. По той же причине использование объекта screens в теле функции относится к имени, объявленному в классе Window_mgr.
С другой стороны, тип возвращаемого значения функции обычно располагается перед именем функции. Когда функция-член определяется вне тела класса, любое имя, используемое в типе возвращаемого значения, находится вне области видимости класса. В результате тип возвращаемого значения должен определять класс, членом которого он является. Например, мы могли бы добавить в класс Window_mgr функцию addScreen(), добавляющую еще одно окно на экран. Этот член класса возвратит значение типа ScreenIndex, которое пользователь впоследствии сможет использовать для поиска этого окна:
class Window_mgr {
public: