Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Базовый и порожденный классы
Из существующих классов можно строить новые классы, которые будут наследовать все свойства этих классов, но будут дополнены своими новыми специфическими свойствами. Итак, пусть определен класс 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; просмотров: 62; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.147.89.24 (0.012 с.) |