- •И. А. Андрианов, д. В. Кочкин, с. Ю. Ржеуцкая
- •Учебное пособие
- •Оглавление
- •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 Методики оптимизации кода
- •Заключение
- •Библиографический список
6. 10 Шаблон "посетитель" (Visitor)
Шаблон "посетитель" предназначен для выполнения различных операций над элементами некоторой коллекции. При этом элементы могут быть различных типов, но являются подклассами одного базового класса, а операции могут выполняться по-разному для различных типов объектов.
Для определения действий, которые необходимо выполнить, требуется знать тип операции и тип объекта. Перед выполнением операции ни один из типов не известен, однако, они определяются при помощи механизма виртуальных функций в элементе структуры и в посетителе. Такой подход получил название двойной диспетчеризации.
В следующем примере имеется набор классов транспортных средств: лошадь, трактор и автомобиль. Каждое транспортное средство имеет две общие операции: получение максимальной скорости и массы, а также по одной уникальной, характерной только для конкретного типа транспортных средств, лошадь − число копыт, трактор − площадь гусениц, автомобиль − количество колес.
// Пример 6.9.1 - шаблон "посетитель"
#include <vector>
#include <iostream>
#include <ctime>
class Visitor;
// Базовый класс для элементов
class Transport
{
public:
virtual ~Transport() = 0 {};
virtual void accept(Visitor* v) = 0;
virtual float maxSpeed() = 0;
virtual float mass() = 0;
};
class Horse : public Transport {
public:
void accept(Visitor* v);
int numHoof() { return 4; }
float maxSpeed() { return 50.f; };
float mass() { return 300.f; };
};
class Tractor : public Transport {
public:
void accept(Visitor* v);
float caterpillarSquare() { return 300.f; }
float maxSpeed() { return 30.f; };
float mass() { return 6000.f; };
};
class Car : public Transport {
public:
void accept(Visitor* v);
int numWheels() { return 4; }
float maxSpeed() { return 150.f; };
float mass() { return 1200.f; };
};
В следующем фрагменте кода представлен базовый класс посетителя − Visitor и набор подклассов, отвечающих за различные операции, выполняемые над транспортными средствами. NumHoofsVisitor − суммарное количество копыт, NumWheelsVisitor − суммарное количество колес, CaterpillarSquareVisitor − общая площадь гусениц, AvgMaxSpeedVisitor − средняя максимальная скорость, TotalMassVisitor − общая масса транспортных средств, CountVisitor − общее количество транспортных средств.
Как легко увидеть, не все запросы могут быть применены ко всем элементам, первые три запроса являются уникальными, остальные три запроса универсальные, они подходят ко всем транспортным средствам.
// Пример 6.9.2 - шаблон "посетитель"
// Интерфейс для посетителей, запросов, операций
class Visitor {
public:
virtual void visit(Horse*) = 0;
virtual void visit(Tractor*) = 0;
virtual void visit(Car*) = 0;
};
// Посетитель для расчета количества копыт
class NumHoofsVisitor : public Visitor {
int numHoofs;
public:
NumHoofsVisitor() : numHoofs(0) {}
int getNumHoofs() { return numHoofs; }
virtual void visit(Horse* t) {
numHoofs += t->numHoof(); }
virtual void visit(Tractor* t) {}
virtual void visit(Car* t) {}
};
// Посетитель для расчета количества колес
class NumWheelsVisitor : public Visitor {
int numWheels;
public:
NumWheelsVisitor() : numWheels(0) {}
int getNumWheels() { return numWheels; }
virtual void visit(Horse* t) {}
virtual void visit(Tractor* t) {}
virtual void visit(Car* t) {
numWheels += t->numWheels(); }
};
// Посетитель для расчета суммарной площади гусениц
class CaterpillarSquareVisitor : public Visitor {
float square;
public:
CaterpillarSquareVisitor() : square(0) {}
float getSquare() { return square; }
virtual void visit(Horse* t) {}
virtual void visit(Tractor* t) {
square += t->caterpillarSquare(); }
virtual void visit(Car* t) {}
};
// Посетитель для расчета средней максимальной скорости
class AvgMaxSpeedVisitor : public Visitor {
float speed;
int count;
public:
AvgMaxSpeedVisitor() : speed(0), count(0) {}
float getSpeed() {
return count == 0 ? 0 : speed / count; }
virtual void visit(Horse* t) {
speed += t->maxSpeed(); ++count; }
virtual void visit(Tractor* t) {
speed += t->maxSpeed(); ++count; }
virtual void visit(Car* t) {
speed += t->maxSpeed(); ++count; }
};
// Посетитель для расчета суммарной массы
class TotalMassVisitor : public Visitor {
float mass;
public:
TotalMassVisitor() : mass(0) {}
float getMass() { return mass; }
virtual void visit(Horse* t) { mass += t->mass(); }
virtual void visit(Tractor* t) { mass += t->mass(); }
virtual void visit(Car* t) { mass += t->mass(); }
};
// Посетитель для расчета количества транспортных средств
class CountVisitor : public Visitor {
int count;
public:
CountVisitor(): count(0) {}
int getCount() { return count; }
virtual void visit(Horse* t) { ++count; }
virtual void visit(Tractor* t) { ++count; }
virtual void visit(Car* t) { ++count; }
};
// Реализация методов для приема посетителей транспортными средствами
void Horse::accept(Visitor *v) { v->visit(this); }
void Tractor::accept(Visitor *v) { v->visit(this); }
void Car::accept(Visitor *v) { v->visit(this); }
Класс TransportDB представляет собой коллекцию транспортных средств − массив указателей и механизм применения посетителей ко всем элементам − метод accept().
// Пример 6.9.3 - шаблон "посетитель"
// База транспортных средств. Хранит транспортные средства и
// предоставляет доступ к ним для посетителей.
class TransportDB
{
std::vector<Transport*> mTransport;
public:
// Заполнение контейнера случайными транспортными средствами
TransportDB() {
srand(time(0));
for(int i = 0; i != 100; ++i) {
int t = rand( ) % 3;
if(0 == t) mTransport.push_back(new Horse());
if(1 == t) mTransport.push_back(new Tractor());
if(2 == t) mTransport.push_back(new Car());
}
}
// Принимает некоторого посетителя v (Visitor*).
// Перенаправляет запрос к каждому элементу.
// ! Тип запроса (Visitor) и
//тип элемента (Transport) не известны.
void accept(Visitor* v) {
for(std::vector<Transport*>::iterator it =
mTransport.begin();
it != mTransport.end();
++it)
{
// Будет вызвана функция accept
//у конкретного подкласса
(*it)->accept(v);
}
}
};
В тестовом примере происходит создание посетителей, применение их ко всем элементам коллекции и вывод результатов.
// Пример 6.9.4 - шаблон посетитель
int main()
{
CountVisitor count;
NumHoofsVisitor numHoofs;
NumWheelsVisitor numWheels;
CaterpillarSquareVisitor catSquare;
AvgMaxSpeedVisitor avgMaxSpeed;
TotalMassVisitor totalMass;
TransportDB db;
db.accept(&count);
db.accept(&numHoofs);
db.accept(&numWheels);
db.accept(&catSquare);
db.accept(&avgMaxSpeed);
db.accept(&totalMass);
std::cout << "CountVisitor: " <<
count.getCount() << std::endl;
std::cout << "NumHoofsVisitor: " <<
numHoofs.getNumHoofs() << std::endl;
std::cout << "NumWheelsVisitor: " <<
numWheels.getNumWheels() << std::endl;
std::cout << "CaterpillarSquareVisitor: " <<
catSquare.getSquare() << std::endl;
std::cout << "AvgMaxSpeedVisitor: " <<
avgMaxSpeed.getSpeed() << std::endl;
std::cout << "TotalMassVisitor: " <<
totalMass.getMass() << std::endl;
system("pause");
}
Результат работы программы следующий:
CountVisitor: 100
NumHoofsVisitor: 176
NumWheelsVisitor: 104
CaterpillarSquareVisitor: 9000
AvgMaxSpeedVisitor: 70
TotalMassVisitor: 224400
Как легко убедиться шаблон посетитель позволяет добавлять операции элементам коллекции в зависимости от типа объекта. Без использования данного шаблона эти операции могли быть добавлены в виде методов класса TransportDB, что привело бы к увеличению его размеров.
