Г. Шилтд - Самоучитель C++ (DJVU) (1114955), страница 16
Текст из файла (страница 16)
Таким образом, в отличие от функции-члена шус!авв, в котором можно непосредственно упоминать переменные п и д, дружественная функция может иметь доступ к этим переменным только через объект, который объявлен внутри функции или передан ей. В предыдущем разделе был рассмотрен важный момент. Когда функция-член использует закрытый элемент класса, то это делается непосредственно, поскольку функция-член выполняется только с объектами данного класса. Таким образом, если в функции-члене класса упоминается закрытый элемент этого класса, то компилятор знает, к какому объекту этот закрытый элемент относится, поскольку знает, с каким объектом при вызове функции-члена связана функция.
Однако дружественная функция не связана с каким бы то ни было объектом. Просто ей предоставлен доступ к закрытым элементам класса. Поэтому дружественная функция не может работать с закрытыми членами класса без упоминания конкретного объекта. Поскольку дружественные функции — это не члены класса, им обычно передается один или более объектов класса, для которого они являются дружественными.
Так было сделано, например, в случае с функцией Ыас!ого Ей был передан объект оЬ класса шус!авв. Поскольку функция !аЬс!ого дружественна для класса вус!авв, она может иметь доступ к закрытым элементам этого класса через объект оЬ. Случись так, что функция Иас1ог9 не была бы дружественной для класса вус!авв, она не имела бы доступа ни к объекту аЬ.п, ни к объекту оЬА!, поскольку переменные п и д — это закрытые члены класса щус!ааа. Дружественная функция — это не член класса и она не может быть задана через имя объекта. Она должна вызываться точно так же, как и обычная функция. Дружественная функция не наследуется. Поэтому если в базовый класс включается дружественная функция, то эта дружественная функция не является таковой для производных классов.
Другим важным моментом, относящимся к дружественным функциям, является то, что функция может быть дружественной более чем к одному классу. Само ителв бч+ 1. Обычно дружественная функция бывает полезна тогда, когда у двух разных классов имеется нечто общее, что необходимо сравнить. Например, рассмотрим следующую программу, в которой создаются классы саг (легковая машина) и 1гвск (грузовик), причем оба содержат в закрытой переменной скорость соответствующего транспортного средства: $1пс1пг(е <1ояЕгеаш> ця1пд пашеярасе яЕс(; с1аяя Ггцсйл I/ предварительное объявление с1аяя саг ( Епг раяяепдегя; 1пг яреес1) рпЫ1с: саг(1пг р, Епг я) Е раяяепдегя .= р; яреес1 = я; ) Егуепй Егг вр дгеасег (сагс, Егцс)< Е) с1аяя тгпс)< ( (пг в е)яЫ; (пг яреес$; рцЬ11с: ггис)г()пг ъч, (пг я) ( ве(яМ = ьч яреес( = я; Ег(епг( (пг яр дгеасег(саг с, тгпсК Е); У* Возвращает положительное число, если легковая машина быстрее грузовика.
Возвращает 0 при одинаковых скоростях. Возвращает отрицательное число, если грузовик быстрее легковой машины. */ Епг яр дгеагег (саг с, ггцс)г г) 1 гесцгп с, яреес( — Е. яреей) ! Епг та1п() 1пг саг с1(б, 55), с2 (2, 120); Егцс)< т1 (10000, 55), Е2 (20000, 72); соне « ьдравнение значений с1 и Е1;~п"; = яр дгеаЕег (с1, е1); (Е(1<0) сои( « "Грузовик быстрее. '~п"; е1яе ЕЕ(г — 0) сопт « "Скорости машин одинаковы. ~п" з е1яе сонг « "Легковая машина быстрее. ~п"; Глава 3. Подробное изучение классов сощ « "~псравненне значений с2 и Е2:~п"; 1 = яр 1геаГег~с2, 2)1 1г~е<0~ сове « "Грузовик быстрее. ~п"; е1яе 1Г ~с==с( сопс « "Скорости машин одинаковы. ~п"1 е1яе сопс « "Легковая машина быстрее. ~п"; геспгп О; В этой программе имеется функция вр ягеа1егО, которая дружественна для классов саг и 1гвсК.
(Как уже установлено, функция может быть дружественной двум и более классам,) Эта функция возвращает положительное число, если объект саг движется быстрее объекта 1гисК, нуль, если их скорости одинаковы, и отрицательное число, если скорость объекта 1гвс11 больше, чем скорость объекта саг. Эта программа иллюстрирует один важный элемент синтаксиса С++ — лредварнтельное объявление (уог1гагг) бес1агавоп), которое еще называют ссылкой вперед (~оыаЫ Яегепсе). Поскольку функция вр дгеа$ег() получает параметры обоих классов саг и 1гиек, то логически невозможно объявить и тот и другой класс перед включением функции ар ягеаМг() в каждый из них. Поэтому необходим иной способ сообщить компилятору имя класса без его фактического объявления.
Этот способ и называется предварительным объявлением. В С++, чтобы информировать компилятор о том, что данный идентификатор является именем класса, перед первым использованием имени класса вставляют следующую строку: с1ааа июг класса; Например, в предыдущей программе предварительным объявлением является инструкция с1аяя 1гисК; после которой класс 1гцск можно использовать в объявлении дружественной функции ар агеа1егО без опасения вызвать ошибку компилятора. 2. Функция может быть членом одного класса и дружественной другому. Например, если переписать предыдущий пример так, чтобы функция яр ягеа1егО являлась членом класса саг и дружественной классу 1гвсй, то получится следующая программа: г"1пс1пйе<ъояс=еатп> пяупд пашеярасе в-б; с1аяя ьгпск; О предварительное объявление с1аяя саг ( 1гп раяяепдегя; 1пс ярееб; Самоучитель С++ рп)з11с: саг(1пп р, 1пя я) ( раяяепдегя = р; яреей = я; 1пп яр дгеапег(1гцс1 1); с1аяя пхпс)< ( 1пю ие1дпо; 1п яюеей; рц)з11с: юппер(1П~ ы, 1пя я) ( ые1дЫ = и; яреес( = я; отметьте новое использование / / оператора расширения области видимости йг1епб 1пя саг;:вр дгеаеег(йгпсМ П); /* Возвращает положительное число, если легковая машина быстрее грузовика .
Возвращает 0 при одинаковых скоростях . Возвращает отрицательное число, если грузовик быстрее легковой машины. */ 1пя Сап::яр дгеа1ег(1гпс)» П) ( /'» Поскольку функция яр дгеа1Ег() — это член класса саг, ей должен передаваться только объект пгпс). */ геппгп ярееб — 1. яреес(» 1ппша1п () ( гп саг с1 (б, 55), с2 (2, 120); пгпс). п1 (10000, 55), п2(20000, 72) сопя « "Сравнение значений с1 и П1:~п"; и = с1 . зр дгеапег(п1); // вызывается как функция-член класса саг 1Г(1<0) соп « "Грузовик быстрее. М"; е1яе 1г"(~ О) сопя << "скорости машин одинаковы. 1п"; е1ве сопя « "Легковая машина быстрее .
'~п" р оопп « "~пСравнение с2 и й2:~п"; = с2 .яр дгеа~ег(й2); // вызывается как функция-член класса саг 1й(я<0) сопя « "Грузовик быстрее. 1п"; е1яе 1Г(к==О) оопп « "скорости машин одинаковы. е1яе оопп « "Легковая машина быстрее . геппгп О; Глава 3. Подробное изучение классов Обратите внимание на новое использование оператора расширения области видимости, который имеется в объявлении дружественной функции внутри объявления класса 1гвс(г. В данном случае он информирует компилятор о том, что функция вр ягва1егО является членом класса саг.
Существует простой способ запомнить, где в такой ситуации нужно указывать оператор расширения области видимости: сначала идет имя класса, потом — оператор расширения области видимости и последним — имя функции-члена. Таким образом член класса будет полностью задан. Фактически при упоминании в программе члена класса никогда не помешает полностью (с именем класса и оператором расширения области видимости) задать его имя. Однако при использовании объекта для вызова функции- члена или для доступа к переменной-члену, полное имя обычно излишне и употребляется редко.
Например, инструкция т = с1.яр Чхеаеег(п1); может быть написана с указанием (избыточным) оператора расширения об- ласти видимости и именем класса саг: т = с1.сап".:яр опеаеег)С1) Поскольку объект с! является объектом типа саг, компилятор уже и так зна- ет, что функция яр р'ва1ег() — это член класса саг, что делает необязатель- ным полное задание имени класса. с)аяя рг1 ( пя рг1пя1пд; 1!...
риЬ1 'с: рг1)) ( рпьпязпц = О; чогя1 яея ргъп~ (1пс яяаяпя) уl... рпьпп(пд = япяппя; с)аяв рг2 ( (пт рг1пт(пя; У/... 1, Представьте себе ситуацию, в которой показанные ниже два класса рг1 и рг2 используют общий принтер, а для оставшейся части программы необходимо знать, когда принтер занят объектом одного из этих классов.
Создайте функцию ггпыеО, которая возвращает (гве, когда принтер занят объектом одного из классов и Ыве — в противном случае. Сделайте эту функцию дружественной как классу рг1, так и классу рг2. Самауситель С++ 104 рпЬ11с: Рп2 () ( Рпгппгпд — — О; ) лоЫ яег рпьпП(ъпЬ яеаопя) /т'... ( рт(ппьпд = япагпя) ) о(аяя яапзр с)опЬ1е *Р~ рпЬ11с: яагер(с(опЬ1е о) ( Р = (ЙопЬ1е *) та11оо(я1геоГ(с(опЬ1е))у 11(!Р) ек1Ь(1); //ошибка выцелеиия памяти *р -яатгр() ( ггее(р); // /~ яапр оЬ1 (123.