
- •Введение в ооп
- •Алгоритмическое и объектно-ориентированное программирование
- •3.Основные понятия объектно-ориентированного программирования
- •4. Объекты и их атрибуты
- •5. Структура визуальной среды
- •4. Страж кода (Code Guard).
- •7. Синтаксис и семантика классов
- •8. Доступ к элементам класса (объекта)
- •9. Встраиваемые функции-элементы
- •10. Создание экземпляров класса. Конструктор
- •Создание объекта в динамической памяти
- •11. Уничтожение объектов класса. Деструктор
- •12. Статические компоненты класса
- •13. Постоянные и модифицируемые элементы-данные
- •14. Передача адреса объекта компонентной функции. Указатель this
- •15. Конструктор копии
- •16. Перегрузка функций и операций
- •17. Операция присваивания для объектов
- •18. Функции преобразования типа объекта
- •19. Наследование. Общие положения
- •Влияние ключа доступа при наследовании
- •20. Простое наследование
- •21. Множественное наследование
- •22. Виртуальное наследование
- •23. Виртуальные функции
- •24. Динамический полиморфизм
- •25. Абстрактные классы
- •26. Дружественные функции и классы
- •27. Шаблоны
- •28. Стандартная библиотека шаблонов. Общие положения
- •29. Примеры контейнеров
- •30. Обработка исключительных ситуаций. Общие положения
- •Синтаксис и семантика генерации и обработки исключительных ситуаций
- •32. Использование объекта класса для возврата параметров исключений
- •33. Ограничение перечня возбуждаемых исключительных ситуаций
- •34. Повторное возбуждение исключительной ситуации. Вложенные блоки try
- •35. Организация проектировани и документирования разработки объектно-ориентированных программ
- •1. Отображение классов.
- •2. Отображение отношений между классами
20. Простое наследование
При простом наследовании производный класс наследует один предшествующий ему по иерархии базовый или промежуточный базовый класс. Схематично простое наследование будет иметь вид:
Синтаксис описания производного класса при простом наследовании:
class имя_производного_класса: ключ_доступа имя_базового_класса { };
Пример:
class A { private: int a;
public: A (int b) {a = b;} // Конструктор
};
class B: public A
{ private: int c, d;
public: B (int i, int j):A (j) {с = i; d = j; } // Конструктору предшествующего базового
// класса передается параметр j
// (используется список инициализации )
}; B ob(10,15);
В результате инициализации глобального объекта ob его элементы-данные будут иметь значения:
а = 15; c = 10; d = 15.
Если при создании объектов осуществляется инициализация компонентных данных (конструкторы с параметрами), то при создании производного класса должна быть предусмотрена последовательная передача всех аргументов всем инициализирующим конструкторам. При этом в конструкторе производного класса обеспечивается передача все параметров конструктору наследуемого класса, который в свою очередь передает параметры далее вверх по схеме наследования.
21. Множественное наследование
При множественном наследовании производный класс наследует несколько базовых классов. Схематично:
Пример:
class A { int a; public: A(int i){ a=i; } };
class B {int b; public: B(int j){ b=j; }};
class C: public A, public B {int c; public: C(int i, int j, int k): A(i),B(j) {c=k;} };
В данном примере конструктор производного класса передает параметры конструкторам всех наследуемых классов.
Передача параметров:
C A B;
Вызов конструкторов:
A( ) B( ) C( )
Вызов деструкторов:
~C( ) ~B( ) ~A( )
22. Виртуальное наследование
Рассмотрим следующую иерархию классов:
При создании объекта производного класса D3 в результате последовательной работы конструкторов будут созданы две копии объектов базового класса В в классах D1 и D2. Возникает неоднозначность, какую копию взять для создания объекта класса D3. Для устранения неоднозначности базовый класс В наследуется в D1 и D2 как виртуальный. Для этого перед ключом доступа ставится ключевое слово virtual.
Пример:
class B { };
class D1: virtual public B { };
class D2: virtual public B { };
class D3: public D1, public D2 { };
В этом случае создаётся одна копия объекта базового класса.
23. Виртуальные функции
Одним из способов обеспечения полиморфного поведения объектов является использование аппарата виртуальных (полиморфных) функций.
Виртуальная функция – это функция, вызов которой зависит от типа (класса) объекта, для которого она вызывается. Это позволяет родительским функциям обращаться к функциям потомков. В отличие от обычных (функции-элементы, в том числе перегруженные, дружественные, внешние), виртуальные функции связываются с вызовом во время выполнения программы, когда ей передается конкретный объект класса. Другими словами, вызов полиморфной функции в исходном тексте программы лишь обозначается, без точного указания на то, какая именно функция вызывается. Такой процесс известен как позднее связывание.
Виртуальная функция является элементом класса, объявляется в базовом классе с использованием ключевого слова virtual. В производном классе виртуальная функция переопределяется. При этом заголовок функции не изменяется, а тело переопределённой функции может быть совершенно другим.
Деструкторы целесообразно объявлять виртуальными, если создаются производные классы.
class B { . . . virtual void func(){} . . . };
class C: public B { . . . void func(){} . . . };
class D: public B { . . . void func(){} . . . };
void main(){
B obB; C obC; D obD;
obB.func(); // Вызов функции базового класса B
obC.func(); // Вызов переопределенной функции производного класса С
obD.func(); // Вызов переопределенной функции производного класса D
}
Алгоритмы функции func( ) в классах B, C и D могут быть различными.
Следующие примеры демонстрируют отличие в использовании перегруженной и полиморфной функций.
Пример 1.
class A
{ public: double f1 (double x) { return x*x; }
double f2 (double x) { return f1(x)/2; }
};
class B: public A
{ public: double f1 (double x) { return x*x*x; } // Перегрузка функции f1
};
void main( )
{ B b; cout << b.f2(3) << endl;
}
Результат работы программы: 4.5.
В данном примере класс parent содержит компонентные функции f1 и f2, причем f2 вызывает f1. класс B, производный от класса A, наследует функцию f2, однако переопределяет (перегружает) функцию f1. Вместо ожидаемого результата 13.5 программа выдает результат 4.5. Дело в том, что компилятор оттранслирует выражение b.f2(3) в обращение к унаследованной функции A::f2, которая, в свою очередь, вызовет функцию A::f1, а не B::f1.
Пример 2.
class A
{ public: virtual double f1 (double x) { return x*x; }
double f2 (double x) { return f1(x)/2; }
};
class B: public A
{ public: virtual double f1 (double x) { return x*x*x; }
};
void main( )
{B b; cout << b.f2(3) << endl;
}
Результат работы программы: 13.5.
В данном примере поддерживается полиморфное поведение объекта. Использование виртуальной функции и ее переопределение в производном классе обеспечивает вызов именно той версии функции f1, которая соответствует объекту, для которого она вызывается.