И.А. Волкова, А.В. Иванов, Л.Е. Карпов - Основы объектно-ориентированного программирования. Язык программирования С++ (1114893), страница 11
Текст из файла (страница 11)
наследование — открытое.return 0;}При необходимости открытого наследования членов базового типа,если тип-наследник описывается с использованием класса, следует явноуказывать квалификатор public:class C: public A{int z;};ЗамечаниеВ других системах программирования, связанных, например, с языком Java, непредусмотрен квалификатор доступа при наследовании, так как в языке Javaпредусмотрен только открытый способ наследования.Защищенный вид доступа (protected) означает, что члены базового типав типе-наследнике доступны только для методов своего (базового) типа, а72Одиночное наследованиетакже для методов производного типа.
Во всех остальных случаях они ведутсебя так же, как члены с закрытым видом доступа (private).Пример:struct A{int x,y;};class B: protected A{int z;public:void putx (int ap){x = ap;}};int main(){B b1;b1.putx(1);. . .return 0;}Ограничение видимости при наследовании ограничивает манипуляциис членами базового типа только в объектах типа-наследника и его потомках.Поэтому при преобразовании указателя типа-наследника к указателю наобъекты базового типа работа с объектом осуществляется в соответствиис правилами видимости для базового класса.Пусть указатель на объект типа-наследника при защищенном наследовании преобразован к указателю на объекты базового типа. Тогда работас объектом типа-наследника с использованием указателя на объекты базового типа происходит в соответствии с правами доступа для базового типа(как уже было указано, через такой указатель виден не весь объект типа-наследника, а только его часть, соответствующая базовому типу):struct A{int x; int y;};struct B: A{int z;};class C: protected A{int z;};int main(){A a;A* pa;B b;C c;C* pc = &c;73Одиночное наследованиеb.x = 1;//pc -> z;b.y = 2;//pc -> x;b.z = 3;pa = (A*)pc;a = b;pa -> x=4;return 0;// ошибка: доступ к закрытому полю// ошибка: доступ к закрытому полю// см.
примечание далее.// правильно: поле A::x - открытое}ПримечаниеТак как в данном примере наследование — защищенное, то при присвоенииуказателя производного типа указателю базового типа требуется явное преобразование (pa=(A*)pc;). При открытом наследовании возможно простоеприсвоение указателей (pa=pc). Это связано с тем, что указатель кроме адресасодержит информацию об объекте.
При защищенном наследовании изменяется не только состав членов класса, но и права доступа.Далее вопросы наследования будут рассмотрены на основе классов.При необходимости данные сведения могут быть переработаны для структурс учетом области доступа и квалификатора доступа при наследованиив структурах по умолчанию.10.4. Закрытое (private)наследованиеЗакрытые члены базового класса недоступны напрямую с использованиемдополнительных методов класса-наследника (при любом способе наследования). Работа внутри класса-наследника с такими получаемыми закрытымичленами базового класса возможна только с использованием открытыхи защищенных методов базового класса.Закрытые и защищенные получаемые методы недоступны для манипулирования с объектом вне класса.
Они могут использоваться как подпрограммы другими методами класса.При закрытом наследовании открытые и защищенные члены базовогокласса (любые) доступны только внутри производного класса и недоступныизвне (через объекты производного класса), как и его собственные закрытыечлены.Таким образом, приведенная таблица показывает вид доступа для членов в типе наследнике для типа наследника следующего уровня. Но, для текущего типа-наследника доступность зависит от вида доступа в базовом типе.Пример:class X1{int ix1;public:74Одиночное наследование};int f1() { .
. . }. . .class Y1: protected X1{. . .};class Z1: public Y1{. . .};class X2{protected:int ix2;public:int f2 () { . . . }. . .};class Y2: X2 {. . .};class Z2: public Y2 {. . .};В классе Y1 переменная ix1 недоступна непосредственно, так как онав базовом классе X1 находится в закрытой области. Однако она может бытьиспользована в функции f1(). в то же время функция f1() не может использоваться в качестве внешнего метода по отношению к объекту, созданному наоснове класса Y1, так как она находится в защищенной области класса Y1. Этоже остается справедливым и для класса Z1. в классе Y2 переменная ix2,в отличие от переменной ix1 в классе Y2, доступна непосредственно.
В классеZ2 переменная ix2 становится недоступной для непосредственного использования, так же, как и переменная ix1 в классе Z1. Другие отличия классов Z1и Z2: в отличие от функции f1() в классе Z1, функция f2() в классе Z2 доступнав классе Z2 качестве внутренней подпрограммы, только для функций, унаследованных из класса Y2, так как в классе Y2 она находится в закрытой области.Закрытое наследование целесообразно в том случае, когда меняетсясущность нового объекта.Пример:Базовый класс описывает фигуры на плоскости и имеет методы вычисления площади фигур, а класс-наследник описывает объемные тела, например, призмы с основанием — плоской фигурой, описываемой базовымклассом.
в этом случае объем тела, описываемого классом-наследником,вычисляется умножением площади основания на высоту. При этом не имеетзначения, каким образом получена площадь основания. Кроме того, методыработы с объемными объектами отличны от методов работы с плоскимиобъектами. Поэтому в данном случае не имеет смысла наследование методовбазового класса для работы с объектами, описываемыми классом-наследником:#include <iostream>75Одиночное наследованиеusing namespace std;class twom{double x,y;public:twom ( double x1=1, double y1=1 ): x(x1), y(y1) {}double sq(){return x*y;}};class thm: private twom{double z;public:thm ( double x1 = 1, double y1 = 1,double z1 = 1): twom(x1,y1), z(z1) {}double vol(){return sq()*z;}};int main(){thm t1 ( 1, 2, 3 );double d1;d1 = t1.vol();cout << "vol= " << d1 << '\n';return 0;}Таким образом, закрытое наследование несколько напоминает композицию объектов, когда подобъект находится в закрытой области.
Все женеобходимо помнить, что наследование — это совсем другая концепция ассоциирования классов, по многим своим свойствам отличная от агрегации,даже в ее строгом варианте (композиции).10.5. Перекрытие именВ производном классе могут использоваться имена членов класса, перекрывающие видимость таких же имен в базовом классе (overriding). При перекрытии имен при работе с объектом через указатель будет исполняться тотметод, который содержится в классе, используемом в объявлении указателя,независимо от типа объекта, на который указывает указатель.Пример:#include <iostream>using namespace std;class A{public:void f ( int x ){cout << "A::f" << '\n';}76Одиночное наследование};class C: public A{public:void f ( int x ){cout << "C::f" << '\n';}};int main(){A a1;A* pa;C c1;C* pc;pc = &c1;pc -> f(1); // C::fpa = pc;pa -> f(1); // A::f — несмотря на то, что pa указывает// на объект c1 типа класс C.pc = (C*)& a1; // Небезопасное преобразованиеуказателя// на объект базового класса к указателю// на объект производного класса (данное// преобразование должно объявляться// явно, иначе — ошибка).pc -> f(1); // C::f — несмотря на то, что pa указывает// на объект а1 типа класс А,// в общем случае такой вызов некорректен.return 0;}Члены базового класса с именами, совпадающими с именами членовпроизводного класса, доступны в производном классе.
Для доступа к нимнеобходимо указывать квалификатор (имя базового класса) с использованиемоперации «::», так как данные члены находятся в доступной области видимости, которая не совпадает с текущей областью видимости. Также методбазового класса доступен через указатель класса-наследника при условиииспользования квалификатора.Пример:#include <iostream>using namespace std;class A {public:void f ( int x ) { cout<<"A::f"<<'\n'; }};class C: public A{public:void f ( int x ){cout << "C::f" << '\n';}void g(){f (1);A::f (1);}};77Одиночное наследованиеint main(){C c1;C* pc;pc=&c1;pc->A::f (1); // вызов метода базового класса с// использованием указателя// класса–наследника.pc->f (1);pc->g ();return 0;}Таким образом, при перекрытии методы базового класса не «затираются» в классе-наследнике.