Базовый и порожденный классы 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Базовый и порожденный классы



Из существующих классов можно строить новые классы, которые будут наследовать все свойства этих классов, но будут дополнены своими новыми специфическими свойствами.

Итак, пусть определен класс A.

 

class A

{ private: int d1, d2, d3;

public:

  A() {d1 = d2 = d3 = 0;} // конструктор по умолчанию

  A(int x, int y, int z)  {d1 = x; d2 = y; d3 = z;} // конструктор с аргументами

void  PrintA() {cout << d1 << ’ ‘ << d2 << ’ ‘ << d3;}

};

 

Определим новый класс B

        тип порождения

                 

class B: public A[,private A1,...]       // класс B порожден из класса A 

       

       список порождения

{ int  d3, d4;

public:

B();                 // конструктор без аргументов

   B(int, int, int);   // конструктор с аргументами

   int  Sum3();

  void  PrintB();  

};

 

Класс A – базовый, класс B – порожденный. Говорят, что класс B наследует все член-данные и член-функции класса A независимо от того, в какой части базового класса они находятся. Таким образом, класс B – это расширенный класс A.

 

+ все член-функции классов A и B.

 

Но это наследование имеет особенность такого рода: член-функции класса B не имеют прямого доступа к член-данным из части private базового класса. При попытке задать такую функцию в классе B

 

void  B:: PrintB() {cout << d1 << ’ ‘ << d2......}

 

компилятор выдаст сообщение о нарушении принципа инкапсуляции.

В данном случае наследование понимается как возможность доступа член-функциями класса B к приватным данным базового класса A косвенно, то есть через член-функции класса A из части public. Таким образом, вывести член-данные из приватной части класса A можно, используя функцию PrintA() класса A:

 

void  B:: PrintB() {PrintA(); cout << ’ ‘ << d3 << ’ ‘ << d4;}

 

Функции вывода в классах A и B могут иметь и одинаковые имена:

 

void  A:: Print() {cout << d1 << ’ ‘ << d2 << ’ ‘ << d3;}

void  B:: Print() {A:: Print(); cout << ’ ‘ << d3 << ’ ‘ << d4;}

 

Обратите внимание, что, не приписав A:: перед вызовом функции Print() класса A, мы получили бы рекурсивное обращение к функции Print() класса B, что привело бы к бесконечному обращению, так как выхода из рекурсии нет.

Однако не всегда удобно использовать приватные данные базового класса в порожденном таким косвенным способом, то есть через его член-функции. Можно разрешить член-функциям порожденного класса использовать член-данные базового класса непосредственно. Таким данным присваивается тип доступа protected (зашищенный).

class A

    { int d1;

      protected: int  d2, d3;

      public:

      ...

      int  Get1() {return d1;}

      ...

      };

 

В этом случае член-функциям класса B (только им и наследникам класса B) разрешен прямой доступ к член-данным из части protected класса A. Например, в классе B можно определить такую функцию

 

int  B:: FF()  {return  d2 * d4;}

 

Операция :: очень широко используется при наследовании: не только член-функции могут иметь одинаковые имена, но и член-данные (в нашем примере d3), и тогда конфликт неоднозначности разрешается этой операцией.

Например,

 

int  B:: Sum3()   {return  A:: d3 + d3;}

 

А член-функция вывода в классе B может быть определена такой функцией

 

void B:: Print() {cout << Get1() << ’ ‘ << d2 << ’ ‘ << A:: d3 << ’ ‘ << d3 << ’ ‘ << d4;}

 

Перечислим типы доступа для объявленных классов:

а) член-функции класса A имеют доступ ко всем собственным член-данным d1, d2, d3;

б) объекты класса A используют член-функции и член-данные только из части public (но не из protected!);

в) член-функции класа B используют собственные член-данные d3, d4 + d2, d3 из части protected класса A и всё из части public классов A и B;

г) объекты класса B имеют доступ к член-данным и к член-функциям только из части public классов A и B.

Например,

 

void  main()

{ A  x; B  y;

  x.d1 = 3;     // Ошибка: d1 – из части private класса A

x.d2 = 5;     // Ошибка: protected – это не public!

x.Print();    // Это верно: функция Print() из части public класса A

int k = x.Get1(); // Верно:  Get1() из части public A: k = x.d1

int  j = y.Get1(); // Верно: Get1() берет d1 из базовой части объекта y

y.Print(); // функция B::Print() выведет член-данные базовой и порожденной части объекта y

  y.d2 = 7; // Ошибка: член-данное d2 сохраняет тот же тип доступа protected в порожденном классе и

// функции main() недоступно

}

 

Замечание. Тип доступа protected является приватным для любых внешних функций! (в частности для main())

Вернемся к списку порождения. Чем отличаются порождения

 

class  B: public  A {...};

class  B: protected  A {...};

class  B: private  A {...};

 

public – это способ наследования, при котором типы доступа к член-данным и член-функциям базового класса в порожденном сохраняются,

protected – понижает тип доступа public в базовом до protected в порожденном,

private – это способ наследования, при котором типы доступа public и protected базового класса становятся private в порожденном (и могут использоваться только в нем, далее нет). Это делается в частных случаях, чтобы скрыть некоторые классы от дальнейших порождений.

Порожденный класс, в свою очередь, может быть базовым для другого класса. Возможны различные иерархии порождения. Например,

 

Рассмотрим простое наследование. Пусть в каждом классе есть функция с одинаковым именем F() в части public. Имеется определение D d; и вызов d.F(); Из какого класса будет работать функция F()? Действует правило доминирования: одинаковое имя в порожденном классе подавляет имя в базовом. То есть в нашем примере будет работать функция D::F(). Таким образом, дерево наследования при поиске функции просматривается снизу вверх и находится ближайшая к классу D. Но вызвать функцию F() можно из любого класса, используя вновь операцию::.

Например,

 

d.B:: F();   // функция F() из класса B

 

или

 

D  *pd = new  D;

pd –> C:: F(); // вызывается функция F() из класса C для объекта

// класса D, определенного в динамической области.

Замечание. Наследование порожденным классом свойств базового имеет следующие ограничения – наследованию не подлежат:

1)  конструкторы;

2)  деструкторы;

3)  операции new и ‘=’, перегруженные в базовом классе;

4)  отношения дружественности.

 (Тэд Фейсон: «Это как в реальной жизни – друзья ваших родителей не обязательно ваши друзья и наоборот».) Т.е. отношения дружественности не передаются ни вверх, ни вниз по дереву наследования.



Поделиться:


Последнее изменение этой страницы: 2021-12-07; просмотров: 61; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.219.22.107 (0.009 с.)