- •И. А. Андрианов, д. В. Кочкин, с. Ю. Ржеуцкая
- •Учебное пособие
- •Оглавление
- •1. Основы языка 8
- •1.2.2 Простые типы данных 13
- •2. Работа с памятью 73
- •3. Основы объектно-ориентированного программирования 87
- •4.Обработка исключений 114
- •5. Шаблонные функции и классы. Библиотека стандартных шаблонов 130
- •6. Паттерны проектирования 159
- •7. Антипаттерны 211
- •9. Методы отладки и оптимизации кода 242
- •1. Основы языка
- •1.1.2 Понятие проекта
- •1.2 Простые типы данных
- •1.2.1 Понятие типа
- •1.2.2 Простые типы данных
- •1.2.3 Внутреннее представление простых типов
- •1.2.4 Ключевое слово typedef. Тип size_t
- •1.3 Константы и переменные
- •1.3.1 Литералы
- •1. Числовые константы:
- •2. Символьные константы:
- •1.3.2 Переменные
- •1.3.3 Описание переменных
- •1.4. Выражения. Преобразование типов
- •1.4.1 Операнды и операции
- •1.4.2 Приоритет операций
- •1.4.3 Преобразование типов
- •1.5 Ветвления и циклы
- •1.5.2 Циклы
- •1.6 Массивы, строки
- •1.6.1 Основные понятия
- •1.6.2 Встроенные массивы
- •1.6.3 Cтроки. Обработка строк с завершающим нулём
- •1.7 Указатели и ссылки. Связь указателей и массивов. Библиотека cstring
- •1.7.1 Понятия указателя и ссылки
- •1.7.2 Связь между массивами и указателями
- •1.7.3 Библиотека cstring
- •1.8 Использование типов vector и string
- •1.8.1 Шаблонный класс vector
- •1.8.2 Класс string
- •1.9 Структуры и объединения. Битовые поля
- •1.10.1 Понятие функции
- •1.10.2 Описание функции и прототип функции
- •1.11 Параметры функции. Способы передачи параметров
- •1.11.1 Параметры функции и глобальные переменные
- •1.11.2 Способы передачи параметров в функцию
- •1.11.3 Передача массивов в функцию
- •1.11.4 Параметры-константы
- •1.11.5 Значения параметров по умолчанию
- •1.12.1 Указатель на функцию
- •1.12.2 Функции с переменным числом параметров
- •1.12.3 Перегрузка функций
- •1.12.4 Встроенные (inline) функции
- •1.13 Рекурсивные функции
- •1.14 Пространства имён
- •1.15 Директивы препроцессора. Макросы
- •2. Работа с памятью
- •2.1 Управление выделением и освобождением памяти
- •2.1.1 Статическое и динамическое выделение памяти
- •2.1.2 Способы динамического выделения и освобождения памяти
- •2.2 Динамические структуры данных
- •2.2.1 Основные понятия
- •2.2.2 Примеры реализации динамических структур на основе указателей
- •3. Основы объектно-ориентированного программирования
- •3.1 Основные понятия ооп
- •3.2.1 Описание класса
- •3.2.2 Область видимости элементов класса. Инкапсуляция
- •3.2.3 Первые примеры
- •3.3. Конструкторы и деструкторы.
- •3.4 Указатель this
- •3.5 Перегрузка операций
- •3.6 Дружественные функции и классы
- •3.7 Статические элементы класса
- •3.8 Наследование и полиморфизм
- •3.8.1. Основные понятия
- •3.8.2 Одиночное наследование
- •3.8.3 Множественное наследование
- •3.8.4 Конструкторы и деструкторы классов-потомков
- •3.9. Полиморфизм при наследовании классов
- •3.9.1 Механизмы раннего и позднего связывания
- •3.9.2 Абстрактные классы
- •4.Обработка исключений
- •4.1 Основные понятия
- •4.2 Перехват исключений
- •4.3 Поиск обработчика исключений. Раскрутка стека.
- •4.4 Повторное возбуждение исключений
- •4.5 "Аппаратные" и "программные" исключения
- •4.6 Стандартные классы исключений
- •4.7 Спецификация исключений, возбуждаемых функцией
- •4.8 Исключения в конструкторах при наследовании
- •4.9. Исключения в деструкторах
- •5. Шаблонные функции и классы. Библиотека стандартных шаблонов
- •5.1 Шаблонные функции
- •5.2 Шаблонные классы
- •5.3 Специализация шаблонов
- •5.4 Шаблонные параметры шаблонов
- •5.5 Разработка шаблонных классов с настраиваемой функциональностью
- •5.6 Использование шаблонов для вычислений на этапе компиляции
- •5.7 Библиотека стандартных шаблонов (stl) – основные понятия
- •5.8 Последовательные контейнеры. Итераторы
- •5.9. Адаптеры контейнеров
- •5.10 Ассоциативные контейнеры
- •5.11 Алгоритмы
- •6. Паттерны проектирования
- •6.1 Порождающие шаблоны
- •6.2 Структурные шаблоны
- •6.3 Шаблоны поведения
- •6.4 Шаблон "фабричный метод" (Factory method)
- •6.5 Шаблон "одиночка" (Singleton)
- •6.6 Шаблон "итератор" (Iterator)
- •6.7 Шаблон "наблюдатель" (Observer)
- •6.8 Шаблон "пул объектов" (Object pool)
- •6.9 Шаблон "команда" (Command)
- •6. 10 Шаблон "посетитель" (Visitor)
- •6.11 Дополнительные задания
- •6.11.1 Шаблон Iterator
- •6.11.2 Шаблон Observer
- •6.11.3 Шаблоны Command и Observer
- •6.11.5 Шаблон Visitor
- •6.11.5 Разработка класса − контейнера
- •6.11.6 Оценка производительности кода
- •7. Антипаттерны
- •7.1 Программирование методом копирования и вставки (Copy-Paste Programming)
- •7.2 Спагетти-код (Spaghetti code)
- •7.3 Магические числа (Magic numbers)
- •7.4 Бездумное комментирование
- •7.5 Жесткое кодирование (Hard code)
- •7.6 Мягкое кодирование (Soft code)
- •7.7 Золотой молоток (Golden hammer)
- •7.8 Слепая вера (Blind faith)
- •7.9 Ненужная сложность (Accidental complexity)
- •7.10 Божественный объект (God Object)
- •7.11 Лодочный якорь (Boat anchor)
- •7.12 Поток лавы (Lava flow)
- •7.13 Изобретение велосипеда (Reinventing the wheel)
- •7.14 Программирование перебором (Programming by permutation)
- •8.1 Выведение типов
- •8.2 Списки инициализации
- •8.3 Улучшение процесса инициализации объектов
- •8.4 Цикл for по коллекции
- •8.5 Лямбда-функции
- •8.6 Константа нулевого указателя nullptr
- •8.7 "Умные" указатели
- •9. Методы отладки и оптимизации кода
- •9.1 Отладка кода
- •9.1.1 Основные этапы отладки
- •9.1.2 Инструменты и приёмы отладки
- •9.2 Оптимизация кода
- •9.2.1 Рекомендации по выполнению оптимизации
- •9.2.2 Методики оптимизации кода
- •Заключение
- •Библиографический список
3.9.2 Абстрактные классы
Иногда бывают случаи, когда невозможно реализовать какой-либо метод (группу методов) в классе-родителе, но точно известно, что его можно и нужно реализовать во всех классах-наследниках. Например, нельзя нарисовать геометрическую фигуру вообще, но легко изобразить точку, круг, квадрат. Для решения этой проблемы используются абстрактные методы.
Абстрактные методы – виртуальные методы класса без реализации, иначе они называются чистыми виртуальными функциями и записываются следующим образом:
virtual тип_результата имя_метода(параметры) = 0;
Соответственно, реализацию данного метода в классе писать не надо, но и нельзя создавать объекты такого класса, т.к. в принципе не понятно, что делать, если будет вызван метод без реализации. Классы, содержащие хотя бы один абстрактный метод, называются абстрактными классами. От них нельзя создавать объекты, но их очень удобно использовать при организации иерархии классов как основу для создания целого семейства наследников, имеющих общие свойства. Кстати, отметим, что упоминавшийся уже класс ios из библиотеки iostream является абстрактным классом, который используется для хранения информации, общей для классов istream и ostream.
Для примера реализации абстрактного класса и его потомков снова воспользуемся предметной областью геометрических фигур. Абстрактный класс shape содержит абстрактный метод area (площадь фигуры), который легко реализуется в двух наследниках circle и rectangle. Далее в примере представлены полиморфные функции, которые корректно работают с любой геометрической фигурой (количество потомков класса shape можно существенно расширить).
// Пример 3.5 – иерархия классов геометрических фигур.
#include <iostream>
// абстрактный класс - геометрическая фигура
class shape
{protected:
int x,y;
public:
shape(int _x,int _y);
shape();
virtual double area()=0; // чистая виртуальная функция
// нельзя вычислить площадь абстрактной фигуры
};
// первый наследник - круг
class circle:public shape
{private:
int r;
public:
circle();
circle(int _x, int _y, int _r);
double area() override;
};
// второй наследник - прямоугольник
class rectangle:public shape
{private:
int w,h;
public:
rectangle();
rectangle(int _x, int _y, int _w, int _h);
double area() override;
};
// реализация конструкторов предка
shape::shape(int _x, int _y)
{x=_x;y=_y;}
shape::shape()
{x=1;y=1;}
// методы circle
circle::circle():shape(),r(100){}
circle::circle(int _x, int _y, int _r):shape(_x,_y),r(_r){}
double circle::area(){return 3.1415926*r*r;}
// методы rectangle
double rectangle::area(){return w*h;}
rectangle::rectangle():shape(){w=100;h=100;}
rectangle::rectangle(int _x, int _y, int _w, int _h):shape(_x,_y),w(_w),h(_h){}
// Теперь мы можем писать универсальные функции для разных видов фигур
// например, сравниваем площади двух фигур
double compare(shape& x, shape& y){return x.area()-y.area();}
// находим максимальную площадь в массиве из разных фигур
double maxarea(shape **x, int n)
{double m=x[0]->area();
for(int i=1;i<n;i++)
if (x[i]->area()>m) m=x[i]->area();
return m;
}
int main(){
shape *p[2];
p[0]=new circle(100,100,50);
p[1]=new rectangle(100,100,20,30);
for (int i=0;i<2; i++)
std::cout<<p[i]->area()<<std::endl;
std::cout<<maxarea(p,2)<<std::endl;
std::cout<<compare(*p[0],*p[1])<<std::endl;
}
В данной главе мы рассмотрели самые основные понятия объектно-ориентированного программирования на C++. В следующих главах эта тема будет существенно расширена.
