- •Санкт-Петербургский
- •Процедурное программирование
- •Объектно-ориентированное программирование
- •Обобщенное программирование
- •На пути к объектно-ориентированному программированию
- •Абстракция сущностей и процедурный язык программирования
- •Абстрактный тип данных
- •Организация класса
- •Определение и объявление класса
- •Члены класса
- •Маркеры доступа
- •Конструкторы
- •Понятие об объекте
- •Организация кода при работе с классами
- •Статические компоненты класса
- •Дружественные функции и дружественные классы
- •Особенности применения дружественных функций и классов
- •Перегрузка оператров
- •Перегрузка оператора присваивания
- •Реализация перегруженного оператора присваивания для класса Array
- •Перегрузка оператора индексирования
- •Понятие о константной функции
- •Константный вариант перегруженного оператора индексирования
- •Вычисление смешанных выражений
- •Наследование
- •Структура объекта порожденного класса
- •Доступ к элементам базового класса
- •Конструкторы порожденного класса
- •Порядок создания объекта порожденного класса
- •Перегруженный оператор присваивания порожденного класса
- •Вызов виртуальной функции из тела невиртуальной функции
- •Виртуализация функций не-членов класса
- •Идиома невиртуального интерфейса (nvi)
- •Реализация механизма виртуальных функций
- •Накладные расходы при работе с виртуальными функциями
- •Чисто виртуальные функции. Абстрактные базовые классы
- •Виртуальные деструкторы
- •Автономные и базовые классы
- •Дублирование подобъектов
- •Конструкторы при виртуальном наследовании
- •Работа с данными при виртуальном наследовании
- •Обработка исключительных ситуаций
- •Завершение или продолжение
- •Распределение обязанностей между разработчиком и клиентом
- •Генерация исключений
- •Объект исключения
- •Раскрутка стека
- •Спецификации исключений
- •Работа с обработчиками
- •Формат обработчика
- •Пример обработки исключений
- •Современная точка зрения на спецификации исключения
- •Шаблоны функций
- •Объявление и определение шаблона функции
- •Примеры объявлений и определений шаблонов функций
- •Инстанцирование шаблона функции
- •Неявное инстанцирование
- •Явное инстанцирование конкретной функции
- •Структура использования шаблона функции с явным инстанцированием
- •Перегрузка шаблона функции
- •Явная специализация шаблона функции
- •Шаблоны классов
- •Использование шаблона класса
- •Наследование и шаблоны
- •Шаблоны классов и отношение включения
- •Рекурсивное использование шаблонов классов
- •Друзья и шаблоны классов
- •Явная и частичная специализация шаблона класса
- •Алгоритмы
- •Алгоритм for_each
- •Функциональные объекты
- •Алгоритм copy
- •Алгоритм sort
- •Термины и определения
- •.Литература
Вычисление смешанных выражений
В ряде случаев важно иметь возможность записи программного кода следующего вида
Complex c1(1, 2); Complex c2 = c1 + 4; Complex c3 = 5 + c1;
Возможны два подхода к решению проблемы вычисления смешанных выражений:
Использование неявных преобразования типов.
Использование нескольких вариантов перегружающих операторов + . Речь идет о перегрузке перегружаемых операторов optrftor+.
// Первый вариант class Complex { public: Complex(double re = 0, double im = 0); friend Complex operator+(const Complex& lhs, const Complex& rhs); // ... }; // Второй вариант class Complex { public: explicit Complex(double re, double im = 0); friend Complex operator+(const Complex& lhs, const Complex& rhs); friend Complex operator+(double lhs, const Complex& rhs); friend Complex operator+(const Complex& lhs, double rhs); // ... }; Замечание. В черновике последний прототип отсутствует !!!
Зарезервированное слово explicitзапрещает неявное преобразование типа. Возможности у второго варианта по работе со смешанными выражениями такие, как и у первого варианта, но временные объекты не появляются.
Перегруженный оператор ->
В качестве примера рассмотрим применение перегрузки этого оператора для реализации отношения делегирования.
Постановка задачи. Имеется класс A/
class A { public: void fa(); void fab(); void vab(); // ... };
Требуется реализовать класс B, в котором была бы возможность использовать функцииfab() иvab() из классаA. Для этой цели можно воспользоваться перегрузкой оператора ->.
class B { public: B(); void fb(); A* operator->() { return delegate_; } private: A* delegate_; };
Особенности. Бинарный оператор -> перегружен как унарный.
// Код клиента intmain() {Bb;b.fb(); // Обращение к собственной функцииb->fab(); // Делегирование // ... }
Недостатки:
Различный синтаксис обращения к собственным функциям и функциям делегатам.
Возможна избыточность интерфейса.
Наследование
Наследование – специальный языковой механизм, предназначенный для выражения особых отношений между классами. Наследование обычно используется в следующих двух случаях:
Для построения иерархических систем.
Создания новых классов, сходных с уже существующими классами.
Остановимся на формализме, связанном с наследованием. Пусть имеется некоторый класс A, от которого наследуется или порождается классB. Графически это отношение отображается следующим образом.
A
Базовый класс
Порожденный класс
B
В настоящее время общепринятым является направлять стрелку от порожденного класса к базовому.
Различают прямую и косвенную базу. Пусть имеются три класса (A,BиC), связанных отношением наследования
A
B
C
Класс Cимеет два базовых класса (AиB).Причем классBявляется прямой базой, а классA– косвенной базой.
Различают одиночное и множественное наследование. При одиночном наследовании все классы имеют одну прямую базу. При множественном наследовании некоторые классы имеют несколько прямых баз.
В языке C++ имеются три разновидности наследования:
public – наследование,
protected – наследование.
private – наследование.
Вид наследования влияет на степень доступности элементов базового класса для функций – членов порожденного класса.
Прямые базовые классы указываются в заголовке порожденного класса в специальном элементе, который называют базовым списком. В базовом списке указывается также и вид наследования. Например:
class C : public A, private B { // ... };
Базовый список отделяется от остальной части заголовка класса двоеточием. В рассматриваемом примере базовый список порожденного класса Cсостоит из двух элементов (классCимеет две прямые базы). Из первого элемента следует, что классCпорождается путемpublic– наследования от классаA, а из второго – путемprivate– наследования от классаB. Вид наследования допускается не указывать. В этом случае он (вид наследования) может быть определен в соответствии с принципом умолчания. При этом для классов, в заголовке которых используется зарезервированное словоclass, в соответствии с принципом умолчания предполагаетсяprivate– наследование, а для классов, использующих в заголовке словоstruct– предполагаетсяpublic– наследование. Например:
class A : B { // ... }; struct C : D { // ... };
В этом примере класс Aпорождается от классаBс помощьюprivate– наследования, а классCпорождается от классаD–с помощьюpublic– наследования.