Г. Шилтд - Самоучитель C++ (PDF) (1114887), страница 31
Текст из файла (страница 31)
Например, в данном фрагменте:base base_ob;base_ob.setx (1) ; // правильно, поскольку объект base_ob// имеет тип baseвызов функции setx() правилен, поскольку функция setx() — это открытыйчлен класса base,4. Как мы уже узнали, хотя открытые члены базового класса при наследованиис использованием спецификатора private в производном классе становятсязакрытыми, внутри производного класса они остаются доступными. Например, ниже представлена исправленная версия предыдущей программы:// Исправленная версия программы^include <iostream>using namespace std;class base (int x;public :void setx(int n) { x = n; }Глава 7.
Наследование211void showxO { cout « x « '\n'; }};// Класс наследуется как закрытыйclass derived: private base {int y;public:// переменная setx доступна внутри класса derivedvoid setxy(int n, int m) { setx(n); у = га; }// переменная showx доступна внутри класса derivedvoid showxyO { showxf); cout « у « '\n'; }};int main (){derived ob;o b . s e t x y U O , 20) ;ob.showxy();return 0;}.В данном случае функции showx() и setx() доступны внутри производногокласса, что совершенно правильно, поскольку они являются закрытыми членами этого класса.•1. Исследуйте следующую конструкцию:ttinclude <iostream>using namespace std;class mybase {int a, b;public:int c;void setab{int i, int j ) ( a = i; b = j ; }void getab(int Si, int &j ) { i = a; j = b; }class derivedl: pablic mybase {212Самоучитель C++class derived2: private mybase (int rnainOderivedl ol;derived2 o2;int i, j;// ...Какая из следующих инструкций правильна внутри функции mainQ?A.ol.getab(i, j ) ;B.о2.getab(i, j ) ;C.o l .
c = 10;D.o 2 . c = 10;2. Что происходит, когда открытые члены базового класса наследуются как открытые? Что происходит, когда они наследуются как закрытые?3. Если вы этого еще не сделали, попытайтесь выполнить все примеры, представленные в этом разделе. Поэкспериментируйте со спецификаторами доступа и изучите результаты.7.2. Защищенные члены классаКак вы узнали из предыдущего раздела, у производного класса нет доступак закрытым членам базового. Это означает, что если производному классунеобходим доступ к некоторым членам базового, то эти члены должны бытьоткрытыми.
Однако возможна ситуация, когда необходимо, чтобы членыбазового класса, оставаясь закрытыми, были доступны для производногокласса. Для реализации этой идеи в C++ включен спецификатор доступаprotected (защищенный).Спецификатор доступа protected эквивалентен спецификатору private сединственным исключением: защищенные члены базового класса доступныдля членов всех производных классов этого базового класса. Вне базовогоили производных классов защищенные члены недоступны.Спецификатор доступа protected может находиться в любом месте объявления класса, хотя обычно его располагают после объявления закрытых членов (задаваемых по умолчанию) и перед объявлением открытых членов.Ниже показана полная основная форма объявления класса:Глава7.Наследование_213class имя класса {// закрытые члениprotected: // необязательный// защищенныеpublic:// открытые «лениКогда базовый класс наследуется производным классом как открытый(public), защищенный член базового класса становится защищенным членомпроизводного класса.
Когда базовый класс наследуется как закрытый(private), то защищенный член базового класса становится закрытым членомпроизводного класса.Базовый класс может также наследоваться производным классом как защищенный (protected). В этом случае открытые и защищенные члены базовогокласса становятся защищеннымичленамипроизводного класса.(Естественно, что закрытые члены базового класса остаются закрытыми, иони не доступны для производного класса.)Спецификатор доступа protected можно также использовать со структурами.1.
В этой программе проиллюстрирован доступ к открытым, закрытым и защищенным членам класса:finclude <iostream>using namespace std;class samp (// члены класса, закрытые по умолчаниюint а;protected: // тоже закрытые члены класса sampint b;public:int с;samp(int n, int m) { a = n; b = m; }int geta(} { return a; }int getb() { return b; }};int main{){samp ob(10, 20);214_______СамоучительC++// ob.b = 99; Ошибка! Переменная Ь защищена и поэтому закрытаob.c = 30; // Правильно! Переменная с// является открытым членом класса sampcout « ob.geta() « ' ';cout « ob.getbO « ' ' « ob.c « r\n';return 0;Как вы могли заметить, выделенная в комментарий строка содержит инструкцию, недопустимую в функции main(), поскольку переменная b являетсязащищенной и таким образом по-прежнему закрытой для класса samp.2.
В следующей программе показано, что происходит, если защищенные членыкласса наследуются как открытые:^include <iostream>using namespace std;class base {protected:// закрытые члены класса base,int a,b;// но для производного класса они доступныpublic:void setab(int n, int m) { a = n; b = m; }class derived: public base {int c;public :void setc(int n) ( с = n; }// эта функция имеет доступ к переменным а и b класса basevoid showabc 0 {cout « а « ' ' « b « ' ' « с « '\n';int main ( )(derived ob;/* Переменные а и b здесь недоступны, поскольку являютсязакрытыми членами классов base и derivedЧob.setab(l, 2) ;ob.setc (3) ;ob. showabc () ;return 0;Глава7.Наследование_215Поскольку переменные а и Ь в классе base защищены и наследуются производным классом derived как открытые члены, они доступны для использования функциями — членами класса derived. Однако вне двух этих классов онив полной мере закрыты и недоступны.3.
Как упоминалось ранее, если базовый класс наследуется как защищенный,открытые и защищенные члены базового класса становятся защищеннымичленами производного класса. Например, в слегка измененной версии программы из предыдущего примера класс base наследуется не как открытый, акак защищенный:// Эта программа компилироваться не будет^include <iostream>using namespace std;class base {protected:// закрытые члены класса base,int a,b;// но для производного класса они доступныpublic:void setab(int n, int m) { a = n; b = m; }};class derived: protected base ( II класс base наследуется// как защищенныйint с;public:void setcfint n} { с = n; }// эта функция имеет доступ к переменным а и b класса basevoid showabc () {cout « a « ' ' « b « ' ' « с « '\n';\•int main()derived ob;// ОШИБКА: теперь функция setab{)// является защищенным членом класса baseob.setab(l, 2); // функция setab() здесь недоступнаob.setc (3) ;ob .
showabc ( ) ;return 0;Как указано в комментариях, поскольку класс base наследуется как защищенный, его открытые и защищенные элементы становятся защищеннымичленами производного класса derived и следовательно внутри функции main()они недоступны.216Самоучитель C++1. Что происходит с защищенным членом класса, когда класс наследуется какоткрытый? Что происходит, когда он наследуется как закрытый?2. Объясните, зачем нужна категория защищенности protected?3. В вопросе 1 из раздела 7.1, если бы переменные а и b внутри класса myclassстали не закрытыми (по умолчанию), а защищенными членами, изменилсябы какой-нибудь из ваших ответов на вопросы этого упражнения? Если да,то почему?7.3.
Конструкторы, деструкторыи наследованиеБазовый класс, производный класс или оба класса вместе могут иметь конструкторы и/или деструкторы. В этой главе исследуется несколько следствий такого положения.Если у базового и у производного классов имеются конструкторы и деструкторы, то конструкторы выполняются в порядке наследования, а деструктор ы — в обратном порядке. Таким образом, конструктор базового классавыполняется раньше конструктора производного класса. Для деструкторовправилен обратный порядок: деструктор производного класса выполняетсяраньше деструктора базового класса.Последовательность выполнения конструкторов и деструкторов достаточноочевидна. Поскольку базовый класс "не знает" о существовании производного, любая инициализация выполняется в нем независимо от производногокласса и возможно становится основой для любой инициализации, выполняемой в производном классе.
Поэтому инициализация в базовом класседолжна выполняться первой.С другой стороны, деструктор производного класса должен выполнятьсяраньше деструктора базового класса потому, что базовый класс лежит в основе производного. Если бы деструктор базового класса выполнялся первым, это бы разрушило производный класс. Таким образом, деструкторпроизводного класса должен вызываться до того, как объект прекратит своесуществование.Пока что ни в одном из предыдущих примеров мы не передавали аргументыдля конструктора производного или базового класса.
Однако это вполневозможно. Когда инициализация проводится только в производном классе,аргументы передаются обычным образом. Однако при необходимости передать аргумент конструктору базового класса ситуация несколько усложняется. Во-первых, все необходимые аргументы базового и производногоклассов передаются конструктору производного класса. Затем, используяГлава7.Наследование_217расширенную форму объявления конструктора производного класса, соответствующие аргументы передаются дальше в базовый класс.