Наследование
Наследование
Спецификаторы доступа в ситуации без наследовании
Спецификаторы доступа в ситуации с наследованием
Правила наследования
Область видимости
Иными словами:
Правила наследования
Виртуальные методы
Виртуальные методы
Рекомендации
Диаграммы классов на UML Варианты изображения класса
712.00K
Категория: ПрограммированиеПрограммирование

Наследование. Правила наследования

1. Наследование

Наследование является мощнейшим инструментом
ООП и применяется для следующих
взаимосвязанных целей:
исключения из программы повторяющихся
фрагментов кода;
упрощения модификации программы;
упрощения создания новых программ на основе
существующих.
Кроме того, наследование является единственной
возможностью использовать объекты, исходный
код которых недоступен, но в которые требуется
внести изменения.
1

2. Наследование

2

3.

#include <iostream> // Пример Counter CountDn
using namespace std;
class Counter
//базовый класс
{
protected:
unsigned int count;
//счетчик
public:
Counter ( ) : count ( 0 ) { } //конструктор без аргументов
Counter ( int c ) : count ( c ) { }
unsigned int get_count ( ) const
{ return count; }
// возвращает значение счетчика
Counter operator++ ( ) //увеличивает значение счетчика
(префикс)
{ return Counter ( ++count ); }
};
class CountDn : public Counter//производный класс
{
public:
Counter operator-- ( ) //уменьшает значение счетчика
{ return Counter ( --count ); } };
3

4.

int main ( )
{
CountDn c1;
// объект с1
cout << "\n c1=" << c1.get_count ( ); //вывод на
печать
++c1; ++c1; ++c1;
//увеличиваем c1 три раза
cout << "\n c1=" << c1.get_count ( ); //вывод на
печать
--c1; --c1; //уменьшаем c1 два раза
cout << "\n c1=" << c1.get_count ( ); //вывод на
печать
cout << endl;
return 0;
4

5.

Синтаксис наследования
Ключи доступа
class имя : [private | protected | public] базовый_класс
{ тело класса };
class
class
class
class
{ ...
A { ... };
B { ... };
C { ... };
D: A, protected B, public C
};
5

6. Спецификаторы доступа в ситуации без наследовании

6

7. Спецификаторы доступа в ситуации с наследованием

7

8. Правила наследования

Ключ доступа
private
protected
public
Спецификатор в
базовом классе
Доступ в
производном классе
private
нет
protected
private
public
private
private
нет
protected
protected
public
protected
private
нет
protected
protected
public
public
8

9. Область видимости

В наследнике можно описывать новые поля и
методы и переопределять существующие методы.
Переопределять методы можно несколькими
способами.
Если какой-либо метод в потомке должен работать
совершенно по-другому, чем в предке, метод
описывается в потомке заново. При этом он может
иметь другой набор аргументов.
Если требуется внести добавления в метод предка,
то в соответствующем методе потомка наряду с
описанием дополнительных действий выполняется
вызов метода предка с помощью операции доступа
к области видимости.
Если в программе планируется работать
одновременно с различными типами объектов
иерархии или планируется добавление в иерархию
новых объектов, метод объявляется как
виртуальный с помощью ключевого слова virtual.
Все виртуальные методы иерархии с одним и тем
же именем должны иметь одинаковый список
аргументов.
10

10.

Иными словами:
•private элементы базового класса в производном
классе недоступны вне зависимости от ключа.
Обращение к ним может осуществляться только
через методы базового класса.
•Элементы protected при наследовании с ключом
private становятся в производном классе private, в
остальных случаях права доступа к ним не
изменяются.
•Доступ к элементам public при наследовании
становится соответствующим ключу доступа.
11

11. Иными словами:

Если базовый класс наследуется с ключом private,
можно выборочно сделать некоторые его элементы
доступными в производном классе:
class Base{
...
public: void f();
};
class Derived : private Base{
...
public: Base::void f();
};
12

12.

Правила наследования
Важнейшим принципом ООП является наследование.
Класс, который наследуется, называется базовым, а наследуемый
– производным.
В примере класс Derived наследует компоненты класса Base,
точнее, компоненты раздела public, которые остаются
открытыми, и компоненты раздела protected (защищенный),
которые остаются закрытыми.
Компоненты раздела private также наследуются, но являются
недоступными напрямую для производного класса, которому
доступны все данные и методы базового класса, наследуемые из
разделов public и protected.
Для переопределенных методов в производном классе действует
принцип полиморфизма, который будет рассмотрен ниже.
Объекту базового класса можно присвоить объект производного,
указателю на базовый класс – значение указателя на
производный класс.
В этом случае через указатель на базовый класс можно получить
доступ только к полям и функциям базового класса. Для доступа
к полям и функциям производного класса следует привести
(преобразовать) ссылку на базовый класс к ссылке
13
производный на класс.

13. Правила наследования

class Base { // определение базового класса
int i; //private по умолчанию
protected:
int k;
public:
Base(){i=0; k=1;}
void set_i(int n); // установка i
int get_i(){ // возврат i
return i;}
void show(){
cout<<i<<" "<<k<<endl;}
}; //конец Base
class Derived : public Base { // производный класс
int j;
public:
void set_j(int n);
int mul(); //умножение i на k базового класса и на j
производного
}; //конец Derived
//установка значения i в базовом классе
void Base::set_i(int n){
i = n; }
//установка значения j в производном классе
void Derived::set_j(int n){
j = n;}
//возврат i*k из Base умноженного на j из Derived
int Derived::mul(){
/*производный класс наследует функции-члены
базового класса*/
return j * get_i()*k;//вызов get_i() базового
класса
}
int main(){
Derived ob;
ob.set_i(10); //загрузка i в Base
ob.set_j(4); // загрузка j в Derived
cout << ob.mul()<<endl; //вывод числа 40
ob.show(); //вывод i и k, 10 1
Base bob=ob;//присваивание объекта ссылке на
базовой тип
cout<<bob.get_i(); // вывод i
while (!kbhit());
return 0;
}
Переменная i недоступна в производном классе,
переменная k доступна, поскольку находится в
разделе protected.
В производном классе наследуются также
функции get_i(), set_i() и show() класса Base из
раздела public.
Функция show() позволяет получить доступ из
производного класса к закрытой переменной i
производного класса.
В результате выводится 40 10 1 10.
Принцип полиморфизма, состоящий в перегрузке
методов, объявленных в различных классах с
одним и тем же именем и списком параметров,
будет рассмотрен ниже.
14

14.

Простое наследование
class daemon : public monstr{
int brain;
public:
// ------------- Конструкторы:
daemon(int br = 10){brain = br;};
daemon(color sk) : monstr (sk) {brain = 10;}
daemon(char * nam) : monstr (nam) {brain = 10;}
daemon(daemon &M) : monstr (M) {brain = M.brain;}
Если конструктор базового класса требует
указания параметров, он должен быть явным
образом вызван в конструкторе производного
класса в списке инициализации
15

15.

Порядок вызова конструкторов
Конструкторы не наследуются, поэтому производный класс должен
иметь собственные конструкторы. Порядок вызова конструкторов:
Если в конструкторе потомка явный вызов конструктора предка
отсутствует, автоматически вызывается конструктор предка по
умолчанию.
Для иерархии, состоящей из нескольких уровней, конструкторы
предков вызываются начиная с самого верхнего уровня. После
этого выполняются конструкторы тех элементов класса, которые
являются объектами, в порядке их объявления в классе, а затем
исполняется конструктор класса.
В случае нескольких предков их конструкторы вызываются в
порядке объявления.
16

16.

Операция присваивания
const daemon& operator = (daemon &M){
if (&M == this) return *this;
brain = M.brain;
monstr::operator = (M);
return *this;
Поля, унаследованные из класса monstr, недоступны
функциям производного класса, поскольку они
определены в базовом классе как private.
Производный класс может не только дополнять, но и
корректировать поведение базового класса.
Переопределять в производном классе рекомендуется
только виртуальные методы
17

17.

Наследование деструкторов
Деструкторы не наследуются. Если деструктор в производном
классе не описан, он формируется автоматически и вызывает
деструкторы всех базовых классов.
В деструкторе производного класса не требуется явно
вызывать деструкторы базовых классов, это будет сделано
автоматически.
Для иерархии, состоящей из нескольких уровней, деструкторы
вызываются в порядке, строго обратном вызову конструкторов:
сначала вызывается деструктор класса, затем — деструкторы
элементов класса, а потом деструктор базового класса.
18

18.

Раннее связывание
Описывается указатель на базовый класс:
monstr *p;
Указатель ссылается на объект производного класса:
p = new daemon;
Вызов методов объекта происходит в соответствии с
типом указателя, а не фактическим типом объекта:
p->draw(1, 1, 1, 1);
// Метод monstr
Можно использовать явное преобразование типа
указателя:
(daemon * p)->draw(1, 1, 1, 1);
19

19.

Описание и использование виртуальных методов
Если в предке метод определен как виртуальный, метод,
определенный в потомке с тем же именем и набором параметров,
автоматически становится виртуальным, а с отличающимся
набором параметров — обычным.
Виртуальные методы наследуются, то есть переопределять их в
потомке требуется только при необходимости задать отличающиеся
действия. Права доступа при переопределении изменить нельзя.
Если виртуальный метод переопределен в потомке, объекты этого
класса могут получить доступ к методу предка с помощью операции
доступа к области видимости.
Виртуальный метод не может объявляться с модификатором
static, но может быть объявлен как дружественный.
Если в классе вводится объявление виртуального метода, он
должен быть определен хотя бы как чисто виртуальный.
20

20.

Чисто виртуальные методы
- содержит признак = 0 вместо тела:
virtual void f(int) = 0;
- должен переопределяться в производном классе.
Класс, содержащий хотя бы один чисто виртуальный метод,
называется абстрактным.
абстрактный класс нельзя использовать при явном
приведении типов, для описания типа параметра и типа
возвращаемого функцией значения;
допускается объявлять указатели и ссылки на абстрактный
класс, если при инициализации не требуется создавать
временный объект;
если класс, производный от абстрактного, не определяет все
чисто виртуальные функции, он также является абстрактным.
21

21.

Виртуальные методы
virtual void draw(int x, int y, int scale, int position);
monstr *r, *p;
r = new monstr; // Создается объект класса monstr
p = new daemon; // Создается объект класса daemon
r->draw(1, 1, 1, 1); // Вызывается метод monstr::draw
p->draw(1, 1, 1, 1); // Вызывается метод daemon::draw
p-> monstr::draw(1, 1, 1, 1); //Обход механизма виртуальных методов
22

22. Виртуальные методы

Виртуальным называется метод, ссылка на
который разрешается на этапе выполнения
программы
Перевод слова «virtual» в данном значении
«фактический», т.е. ссылка разрешается по факту
вызова
23

23. Виртуальные методы

Множественное наследование
class monstr{
public: int get_health(); ...
};
class hero{
public: int get_health();...
};
class ostrich: public monstr, public hero { ... };
int main(){
ostrich A;
cout << A.monstr::get_health();
cout << A.hero::get_health();
}
24

24.

class monstr{
...
};
class daemon: virtual public monstr{
...
};
class lady: virtual public monstr{
...
};
class baby: public daemon, public lady{
...
};
monstr
daemon
lady
baby
25

25.

Рекомендации
Множественное наследование применяется для того,
чтобы обеспечить производный класс свойствами
двух или более базовых.
Чаще всего один из этих классов является
основным, а другие обеспечивают некоторые
дополнительные свойства, поэтому они называются
классами подмешивания.
По возможности классы подмешивания должны быть
виртуальными и создаваться с помощью
конструкторов без параметров, что позволяет
избежать многих проблем, возникающих, когда у
базовых классов есть общий предок.
26

26. Рекомендации

Диаграммы классов на UML
Варианты изображения класса
27

27. Диаграммы классов на UML Варианты изображения класса

Виды отношений между классами
ассоциация
(два класса концептуально
взаимодействуют друг с
другом);
наследование
(отношение обобщения, «is a»);
агрегация (отношение
целое/часть, «has a»);
строгая (композиция)
нестрогая (по ссылке)
зависимость
(отношение использования)
28
English     Русский Правила