Г. Шилтд - Самоучитель C++ (DJVU) (1114955), страница 31
Текст из файла (страница 31)
Перегруженный оператор == должен возвращать значение йие, если все элементы обоих операндов равны, в противном случае он должен возвращать значение Ыве. 5. Переработайте решение упражнения 4 так, чтобы перегрузить операторы с использованием дружественных функций. 6. Используя класс и функции из вопроса 4, перегрузите оператор ++ с помощью функции — члена класса, а оператор — с помощью дружественной функции.
(Перегрузите только префиксные формы операторов ++ и —.) 7. Можно ли, используя дружественную функцию, перегрузить оператор присваиванияу 7.1. Управление доступом к базовому классу Когда один класс наследуется другим, используется следующая основная форма записи: о1аве вмк пропаводноко класса: оп дососала ммк безоконно класса Г // 3 Здесь сп доступа — это одно из трех ключевых слов: рвЬИс, рпта1е или ргогесгей. Обсуждение спецификатора доступа рго(ес1ед отложим до следующего раздела этой главы. Здесь рассмотрим спецификаторы рвЬИс и рпта1е.
Спецификатор доступа (ассезв зрес)бег) определяет то, как элементы базового класса (Ьазе с1авз) наследуются производным классом (дегп'ед с1азз). Если спецификатором доступа наследуемого базового класса является ключевое слово риЬИс, то все открытые члены базового класса остаются открытыми и в производном. Если спецификатором доступа наследуемого базового класса является ключевое слово рг1уа1е, то все открытые члены базового класса в производном классе становятся закрытыми. В обоих случаях все закрытые члены базового класса в производном классе остаются закрытыми и недоступными. Важно понимать, что если спецификатором доступа является ключевое слово рпта1е, то хотя открытые члены базового класса становятся закрытыми в производном, они остаются доступными для функций — членов производного класса.
Самоучитель С++ 208 и -;:При меу~-~Ь 1. Здесь представлены базовый и наследующий его производный классы (наследование со спецификатором риЬИс): Ипс1пс(е <тоясгвалс ия)пп патеярасе ясс)т с1аья Ьаяе ьп'г. х; рп)з11с: чоЫ яесх(1пс и) ( х = т1; ) чоЫ я)тоих() ( сопб сс х « '~п' р (т' Класс наследуется как открытый с1аяя с(епуес): риы(с Ьаяе ( )пг у; рпЬ11с: чоЫ яесу(гпс п) ( у п; чоЫ яЬоиу () ( сопс « у « '~п'; ) 1пг кати() ( йеггчеб оЬ; оЬ.яеех(10); оЬ.зееу(<0); доступ к члену базового класса // доступ к члену производного класса оЬ.я)тонх().
оЬ.з)тону(); доступ к члену базового класса доступ к члену производного класса гегпгп 0; Как показано в программе, поскольку класс Ьаве наследуется как открытый, открытые члены класса Ьаве — функции яе$хО и зЬеттх9 — становятся открытыми производного класса дептед и поэтому доступны из любой части Технически спецификатор доступа не обязателен. Если спсцификатор доступа не указан и производный класс определен с ключевым словом с1ава, то базовый класс по умолчанию наследуется как закрытый. Если спецификатор доступа не указан и производный класс определен с ключевым словом а1гис1, то базовый класс по умолчанию наследуется как открытый. Тем не менее, для ясности большинство программистов предпочитают явное задание спецификатора доступа. гов Глава Наследование программы.
Следовательно„совершенно правильно вызывать зги функции из функции в)а)во. 2. Важно понимать, что наследование производным классом базового как открытого совсем не означает, что для производного класса станут доступными закрытые члены базового. Например, зто небольшое изменение в классе депре(1 из предыдущего примера неправильно: с1аяя Ьаяе ( 1П' Х; рпЬ11.с: чо1<( яеСх(1пг п) ( х = п' ) чоьс) анодах () ( сонь «х « '1п'р ) Класс наследуется как открытьай с1аяя г)ег1чесн рпЬ11с Ьаяе ( 1пг у; рпЬ11с: чо1С( яегу(1ПГ и) Закрытые члены базового класса недоступны.
х — это закрытый член базового класса и поэтому внутри производного класса он недоступен *l чогг( яном яша() ( соил « х~-у << ''~п'," ) // Ошибка! ! ! чо1с( 5)тону() ( сопб « у « ' 1п'; Здесь в производном классе ()епте(1 сделана попытка доступа к переменной х, которая является закрытым членом базового класса Ьаяе. Это неверно, поскольку закрытые члены базового класса остаются закрытыми, независимо от того, как он наследуется.
3. Ниже представлена слегка измененная версия программы из примера 1. Базовый класс ))аяе наследуется как закрытый, т. е. с ключевым словом рпта1е. Такое изменение, как показано в комментариях, при компиляции ведет к ошибке. П В этой программе сеть ошибка ()1пс1пбе <1оя1геашь пя1пс пагпеярасе яЫ; с1аяя Ьаяе (пг х; рпЬ11с: уоЫ яебх(ьп и) ( х = и; ) уоЫ япоих() ( сонг « х « '1п'; Самоучитель С+ // Класс наследуется как закрытый с1аяя с)еггчеон рг1часе Ьаяе ( 1пс у; рпЬ11с: ч01о зесу(1пс и) ( у = и; чо16 зЬоыу () ( соис << у (< '1п', ) 1пс аагп() Йег1уеб оЬ; оЬ.зе~х(10); оЬ.весу(~О); ОШИБКА — теперь закрыто для производного класса правильный доступ к члену производного класса оЬ.зпоых()Г // ОШИБКА — теперь закрыто для производного класса оЬ, зЬоыу ()„ // правильный доступ к члену производного класса гегигп О~ Как отражено в комментариях к этой (неправильной) программе, функции авО1УХО И аЕ$ХО СтаНОВЯтСЯ ЗаКРЫтЫМИ В ПРОИЗВОДНОМ КЛаССЕ И НЕДОСТУПНЫ- ми вне его.
Запомните, что функции вЬо1ухо и ве(хо в базовом классе Ьазе по-прежнему остаются открытыми независимо от того, как они наследуются производным классом. Это означает, что объект типа ))азе мог бы получить доступ к этим функциям в любом месте программы. Однако для объектов типа ((ег(уей они становятся закрытыми. Например, в данном фрагменте: Ьазе Ьазе оЬ; Ьазе оЬ.зесх(1); // правильно, поскольку объект Ьазе оЬ // имеет тип Ьаяе вызов функции ве$х() правилен, поскольку функция зе$х() — это открытый член класса Ьаве, // Исправленная версия программы ()1пс1иг(е <1озггеагп> пя(пя пагпезрасе зьФ с(аяя Ьаяе ( )пт х; рпЬ11с: уоЫ зеех(1пс и) ( х = и; ) 4.
Как мы уже узнали, хотя открытые члены базового класса при наследовании с использованием спецификатора рпуа1е в производном классе становятся закрытыми, внутри производного класса они остаются доступными. Например, ниже представлена исправленная версия предыдушей программы: Глава 7. Ласлевование ло'с) яЬоых() ( соШ « х « 'М ' ) Класс наслелуегся как закрытый слазя с(елглеа: рлглаве Ьаяе Шс у! рыЬ1гс: геременная яесх доступна внутри класса с)ел' гес) ло(с) яевху(|лс и, гнс п0 ( яесх(п); у = га; ) переменная вЬоих доступна внутри класса с)елз.уес) логс) яЬоыху() ( зЬоых(); сош « у « '~п'з ) глс спала () ( с(еггиес( оЬ; оЬ.яесху(10, 20); оЬ.яЬонху(); гегигп 0; В данном случае функции Э)!Ела(» и М(ЗГ(» доступны внутри производного класса, что совершенно правильно, поскольку они являются закрытыми членами этого класса.
1. Исслсдуйтс следуюшую конструкцию: Н)пс!ис(е <тоя сгеагп> ия(пя пашеярасе яЫ; с1аьв туЬаяе ( )пг а, Ь; рпЬ1лс: (п( сг уоЫ яесаЬ(гпл 1, (пг з) ( а = з.; Ь = 3; ъо(с( дегаЬ(зпЬ %, (пг е1) ( 1 = а; ) = Ь; ) с1аьв с(ег(уес11: раЬ)(с шуЬаве ( // 1! Самоучитель С++ с)аве бееьчес)2: рт1чаге туЬаее ( т')' 1) глав ла).о1) с)ет' теа1 о1; бег'чес12 о2; )по ), Какая из следующих инструкций правильна внутри функции вашо? Л.
о1,Яе~еЬ(Ъ, 3); В. о2.дееаЬ11., 1); С. о1.с = 10; Г). о2.с = 10; 2. Что происходит, когда открытые члены базового класса наследуются как открытые? Что происходит, когда они наследуются как закрытые? 3. Если вы этого еще не сделали, попытайтесь выполнить все примеры, представленные в этом разделе. Поэкспериментируйте со спецификаторами доступа и изучите результаты. ?.2.
Защищенные члены класса Как вы узнали из предыдущего раздела, у производного класса нет доступа к закрытым членам базового. Это означает, что если производному классу необходим доступ к некоторым членам базового, то эти члены должны быть открытыми. Однако возможна ситуация, когда необходимо, чтобы члены базового класса, оставаясь закрытыми, были доступны для производного класса.
Для реализации этой идеи в С++ включен спецификатор доступа рго1ес1ей (защищенный). Спецификатор доступа рго1ес1ед эквивалентен спецификатору рг1уа1е с единственным исключением: защищенные члены базового класса доступны для членов всех производных классов этого базового класса. Вне базового или производных классов защищенные члены недоступны. Спецификатор доступа рго1ес1ей может находиться в любом месте объявления класса, хотя обычно его располагают после объявления закрытых членов (задаваемых по умолчанию) и перед объявлением открытых членов.
Ниже показана полная основная форма объявления класса: Глава Наследование 213 с1авз имя класса 1 // закрытые илаии рхосесЕей: // иеойивателъний спецнфихаиюр // защищенные члеип рпЫлс: откржютме члаци 1. В этой программе проиллюстрирован доступ к открытым, закрытым и защищенным членам класса: Фапс1пде <1овстеюп> пз1пв патпезрасе вЕЙ; с1авв за1пр ( члены класса, закрытые по умолчанию 1пс а; ртосессей: гпс Ь; рпЬ11с: тоже закрытые члены класса запр ва~пр(1пт г., гпс т) 1 а = и; Ь = т; ) 1пс с)еса() 1 пеептп а; ) гпс депЬ)) 1 песыпп Ь; ) плк жатп() 1 еатпр оЬ(10, 20) Г Когда базовый класс наследуется производным классом как открытый (рпЫ(с), зашишенный член базового класса становится защищенным членом производного класса.
Когда базовый класс наследуется как закрытый (рпчй1е), то защищенный член базового класса становится закрытым членом производного класса. Базовый класс может также наследоваться производным классом как защищенный (рго1есФей). В этом случае открытые и защищенные члены базового класса становятся защищенными членами производного класса. (Естественно, что закрытые члены базового класса остаются закрытыми, и они не доступны для производного класса.) Спецификатор доступа рго1ес1ей можно также использовать со структурами. Самоучитель С~+ оЬ.Ь =- 99; Ошибка! Переменная Ь защищена и поэтому закрыта оЬ.с = 30; !1 Правильно! Переменная с является открытым членом класса зашр соне « оЬ.деса() « сенс « оЬ.десЬ() « ' ' « оЬ.с « '~п'> те ытп О; Как вы могли заметить, выделенная в комментарий строка содержит инст- рукцию, недопустимую в функции шашО, поскольку переменная Ь является защищенной и таким образом по-прежнему закрытой для класса швр.