30. Наследование.
Важнейшим свойством объектно-ориентированного программирования является наследование. Для того, чтобы показать, что класс В наследует класс A (класс B выведен из класса A ), в определении класса B после имени класса ставится двоеточие и затем перечисляются классы, из которых B наследует:
class A
{
public:
A();
~A();
MethodA();
};
class B : public A
{
public:
B();
. . .
};
Термин " наследование " означает, что класс B обладает всеми свойствами класса A, он их унаследовал. У объекта производного класса есть все атрибуты и методы базового класса. Разумеется, новый класс может добавить собственные атрибуты и методы.
B b;
b.MethodA(); // вызов метода базового класса
Часто выведенный класс называют подклассом, а базовый класс – суперклассом. Из одного базового класса можно вывести сколько угодно подклассов. В свою очередь, производный класс может служить базовым для других классов. Изображая отношения наследования, их часто рисуют в виде иерархии или дерева.
Рис. 10.1. Пример иерархии классов.
Иерархия классов может быть сколь угодно глубокой. Если нужно различить, о каком именно классе идет речь, класс C называют непосредственным или прямым базовым классом класса D, а класс A – косвенным базовым классом класса D.
Предположим, что для библиотечной системы, которую мы разрабатываем, необходимо создать классы, описывающие различные книги, журналы и т.п., которые хранятся в библиотеке. Книга, журнал, газета и микрофильм обладают как общими, так и различными свойствами. У книги имеется автор или авторы, название и год издания. У журнала есть название, номер и содержание – список статей. В то же время книги, журналы и т.д. имеют и общие свойства: все это – "единицы хранения" в библиотеке, у них есть инвентарный номер, они могут быть в читальном зале, у читателей или в фонде хранения. Их можно выдать и, соответственно, сдать в библиотеку. Эти общие свойства удобно объединить в одном базовом классе. Введем класс Item, который описывает единицу хранения в библиотеке:
class Item
{
public:
Item();
~Item();
// истина, если единица хранения на руках
bool IsTaken() const;
// истина, если этот предмет имеется в библиотеке
bool IsAvailable() const;
long GetInvNumber() const; // инвентарный номер
void Take(); // операция "взять"
void Return(); // операция "вернуть"
private:
// инвентарный номер — целое число
long invNumber;
// хранит состояние объекта - взят на руки
bool taken;
};
Когда мы разрабатываем часть системы, которая имеет дело с процессом выдачи и возврата книг, вполне достаточно того интерфейса, который представляет базовый класс. Например:
// выдать на руки
void
TakeAnItem(Item& i)
{
. . .
if (i.IsAvailable())
i.Take();
}
Конкретные свойства книги будут представлены классом Book.
class Book : public Item
{
public:
String Author(void) const;
String Title(void) const;
String Publisher(void) const;
long YearOfPublishing(void) const;
String Reference(void) const;
private:
String author;
String title;
String publisher;
short year;
}; // автор
// название
// издательство
// год выпуска
// полная ссылка
// на книгу
Для журнала класс Magazine предоставляет другие сведения:
class Magazine : public Item
{
public:
String Volume(void) const;
short Number(void) const;
String Title(void) const;
Date DateOfIssue() const;
private:
String volume;
short number;
String title;
Date date;
};
// том
// номер
// название
// дата выпуска
Ключевое слово public перед именем базового класса определяет, что внешний интерфейс базового класса становится внешним интерфейсом порожденного класса. Это наиболее употребляемый тип наследования. Описание защищенного и внутреннего наследования будет рассмотрено чуть позже.
У объекта класса Book имеются методы, непосредственно определенные в классе Book, и методы, определенные в классе Item.
Book b;
long in = b.GetInvNumber();
String t = b.Reference();
Производный класс имеет доступ к методам и атрибутам базового класса, объявленным во внешней и защищенной части базового класса, однако доступ к внутренней части базового класса не разрешен. Предположим, в качестве части полной ссылки на книгу решено использовать инвентарный номер. Метод Reference класса Book будет выглядеть следующим образом:
String
Book::Reference(void) const
{
String result = author + "\n"
+ title + "\n"
+ String(GetInvNumber());
return result;
}
(Предполагается, что у класса String есть конструктор, который преобразует целое число в строку.) Запись:
String result = author + "\n"
+ title + "\n"
+ String(invNumber);
не разрешена, поскольку invNumber – внутренний атрибут класса Item. Однако если бы мы поместили invNumber в защищенную часть класса:
class Item
{
. . .
protected:
long invNumber;
};
то методы классов Book и Magazine могли бы непосредственно использовать этот атрибут.
Назначение защищенной ( protected ) части класса в том и состоит, чтобы, закрыв доступ "извне" к определенным атрибутам и методам, разрешить пользоваться ими производным классам .
Если одно и то же имя атрибута или метода встречается как в базовом классе, так и в производном, то производный класс перекрывает базовый.
class A
{
public:
. . .
int foo();
. . .
};
class B : public A
{
public:
int foo();
void bar();
};
void
B::bar()
{
x = foo();
// вызывается метод foo класса B
}
Однако метод базового класса не исчезает. Просто при поиске имени foo сначала просматриваются атрибуты и методы самого класса. Если бы имя не было найдено, начался бы просмотр имен в базовом классе, затем просмотр внешних имен. В данном случае имя foo существует в самом классе, поэтому оно и используется.
С помощью записи A::foo() можно явно указать, что нас интересует имя, определенное в классе A, и тогда запись:
x = A::foo();
вызовет метод базового класса.
Вообще, запись класс::имя уже многократно нами использовалась. При поиске имени она означает, что имя относится к заданному классу.
- 25. Понятие объекта и класса.
- Определение методов класса
- Переопределение операций
- Подписи методов и необязательные аргументы
- Запись классов
- 26. Скрытие данных и общий интерфейс.
- 27. Конструкторы и деструкторы Инициализация объектов класса: конструкторы
- Основное назначение конструкторов - инициализация объектов.
- Использование конструкторов с аргументами по умолчанию
- Если параметры не передаются конструктору, в определении объекта не нужно включать пустые круглые скобки.
- Использование деструкторов
- Когда вызываются конструкторы и деструкторы.
- 28. Перегрузка классов.
- Перегрузка операторов плюс и минус
- Второй пример
- Операторы, которые Вbl he можете перегрузить
- 29. Дружественные классы.
- 30. Наследование.
- Внутреннее и защищенное наследование
- Виртуальное наследование
- 31. Множественное наследование.
- 32. Полиморфизм.
- 33. Виртуальная функция.
- 34. Передача данных по умолчанию.
- 39. Создание и уничтожение динамических объектов.
- 40. Динамические библиотеки (dll).