- •И. А. Андрианов, д. В. Кочкин, с. Ю. Ржеуцкая
- •Учебное пособие
- •Оглавление
- •1. Основы языка 8
- •1.2.2 Простые типы данных 13
- •6. Паттерны проектирования 159
- •7. Антипаттерны 211
- •2. Работа с памятью 73
- •3. Основы объектно-ориентированного программирования 87
- •4.Обработка исключений 114
- •5. Шаблонные функции и классы. Библиотека стандартных шаблонов 130
- •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.6 Использование шаблонов для вычислений на этапе компиляции
- •5.5 Разработка шаблонных классов с настраиваемой функциональностью
- •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.5 Шаблон "одиночка" (Singleton)
Шаблон "одиночка" является хорошей заменой глобальным переменным, он обеспечивает существование единственного экземпляра некоторого класса и позволяет получить удобный доступ к этому экземпляру.
В следующем фрагменте кода приведен класс Singleton, который должен иметь только один экземпляр. Для того чтобы гарантировать наличие единственного экземпляра, необходимо исключить любые возможности создания объекта класса пользователем. Для этого необходимо сделать закрытыми конструктор по умолчанию, конструктор копирования по умолчанию и оператор присваивания.
Для обеспечения единой точки доступа к экземпляру создается статическая функция getSingleton(), которая возвращает указатель на единственный экземпляр класса.
// Пример 6.4 - шаблон "одиночка"
#include <iostream>
class Singleton
{
// Закрытые конструкторы и оператор равно препятствуют
// созданию дополнительных экземпляров класса.
Singleton() {};
Singleton(Singleton&) {}
Singleton& operator=(Singleton&) {}
public:
// Единая точка доступа к экземпляру класса. Экземпляр класса
// создается при первом вызове метода.
static Singleton* getSingleton()
{
static Singleton instance;
return &instance;
}
void print()
{
std::cout << "Hello I'm Singleton" << std::endl;
}
};
int main()
{
Singleton::getSingleton()->print();
system("pause");
}
Результат работы программы следующий:
Hello I'm Singleton
Шаблон одиночка имеет множество разновидностей, которые помимо вышеописанных возможностей позволяют контролировать порядок создания взаимозависимых одиночек, процесс и порядок уничтожения экземпляров одиночек. Однако, такие модификации будут более сложными, особенно при большом количестве взаимозависимых экземпляров Singleton.
6.6 Шаблон "итератор" (Iterator)
Шаблон "итератор" предоставляет возможность последовательного перебора элементов коллекции вне зависимости от её внутреннего устройства. Будь то массив, список, стек или дерево, итератор, с точки зрения пользователя, работает одинаково. Благодаря такой унификации, возможно создавать алгоритмы, не зависящие от типа коллекции и допускающие её замену.
Рассмотрим реализацию шаблона "итератор" на примере коллекции "циклический массив" − класс CycleArray. Данный массив имеет заранее заданный фиксированный размер и поддерживает операции индексирования, получения размера и добавления элемента в конец массива. До момента заполнения новые элементы добавляются в конец массива, после заполнения новые элементы переписывают уже имеющиеся. Переписывается элемент, который был добавлен раньше всех остальных. Итератор на начало последовательности возвращается методом begin(), итератор на элемент, следующий за последним, возвращается методом end(). Сам класс Iterator является вложенным в класс CycleArray.
// Пример 6.5 - шаблон "итератор". Обход циклического массива.
#include <vector>
#include <iostream>
// Циклический массив основан на стандартном контейнере std::vector,
// однако элементы в циклическом массиве располагаются иначе.
class CycleArray
{
std::vector<int> mArray;
int mCapacity;
int mFirstElement;
public:
// Вложенный класс итератор, позволяющий получить доступ к элементам
// циклического массива в нужном порядке. Использование итератора
// циклического массива схоже с использованием итератора
// контейнера std::vector.
class Iterator
{
CycleArray* mArray;
int mIndex;
public:
Iterator(CycleArray* arr, int index):
mArray(arr),
mIndex(index)
{}
bool operator!=(Iterator& it){
return mArray != it.mArray || mIndex != it.mIndex;
}
int operator*() {
return mArray->get(mIndex);
}
void operator++() {
++mIndex;
}
};
CycleArray(int capacity):
mFirstElement(-1),
mCapacity(capacity)
{
}
// Добавление элемента в конец циклического массива
void push_back(int value)
{
if(mArray.size() < mCapacity)
{
mArray.push_back(value);
++mFirstElement;
return;
}
++mFirstElement;
mFirstElement = mFirstElement % mCapacity;
mArray[mFirstElement] = value;
}
// Текущий размер циклического массива
int size() {
return mArray.size();
}
// Получение элемента по индексу
int get(int index) {
int i = mFirstElement - index;
if(i < 0)
i = mArray.size() + i;
return mArray[i];
}
// Итератор на первый элемент
Iterator begin() {
return Iterator(this, 0);
}
// Итератор на элемент следующий за последним
Iterator end() {
return Iterator(this, mArray.size());
}
};
int main()
{
CycleArray arr(10);
for(int i = 0; i != 15; ++i)
{
arr.push_back(i);
for(CycleArray::Iterator it = arr.begin();
it != arr.end();
++it)
{
std::cout << *it << ' ';
}
std::cout << std::endl;
}
system("pause");
}
Результат работы программы следующий:
0
1 0
2 1 0
3 2 1 0
4 3 2 1 0
5 4 3 2 1 0
6 5 4 3 2 1 0
7 6 5 4 3 2 1 0
8 7 6 5 4 3 2 1 0
9 8 7 6 5 4 3 2 1 0
10 9 8 7 6 5 4 3 2 1
11 10 9 8 7 6 5 4 3 2
12 11 10 9 8 7 6 5 4 3
13 12 11 10 9 8 7 6 5 4
14 13 12 11 10 9 8 7 6 5
Шаблон "итератор" активно применяется в стандартной библиотеке языка C++ (STL − standard template library) для доступа к элементам контейнеров std::vector, std::deque, std::map, std::set и другим.
В данном примере обход элементов циклического массива в процедуре main() схож с использованием итераторов для обхода элементов контейнеров стандартной библиотеки.
Важным достоинством итераторов является возможность их использования без знания деталей реализации конкретного класса коллекции.
В рассмотренном примере для проверки существования следующего элемента в коллекции используется сравнение текущего итератора с итератором end() при помощи перегруженного оператора "не равно". Иногда такая проверка помещается в отдельный метод итератора, например has_next() или is_done(), в таком случае необходимость в методе end() у класса-коллекции отпадает.
