- •И. А. Андрианов, д. В. Кочкин, с. Ю. Ржеуцкая
- •Учебное пособие
- •Оглавление
- •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 Методики оптимизации кода
- •Заключение
- •Библиографический список
8.1 Выведение типов
Механизм автоматического выведения типов позволяет избавить программиста от необходимости самостоятельно указывать необходимый тип переменной при объявлении. В С++11 вместо типа можно указать ключевое слов auto, и компилятор самостоятельно выведет нужный тип.
// Пример 8.1 - автоматический вывод типов при инициализации переменных
int i = 1;
float f = 1.1;
auto i2 = 2;
auto f2 = 2.2;
std::cout << "i: " << i << ", " << "i2: " << i2 << std::endl;
std::cout << "f: " << f << ", " << "f2: " << f2 << std::endl;
Результат работы программы следующий:
i: 1, i2: 2
f: 1.1, f2: 2.2
Тип данных может быть определен исходя из типа возвращаемого значения функции.
// Пример 8.2 - автоматический вывод типов при вызове функции
const std::string& getString()
{
static std::string s = "Hello";
return s;
}
auto s = getString();
std::cout << s << std::endl;
Результат работы программы следующий:
Hello
При работе с контейнерами стандартной библиотеки ключевое слова auto может быть использовано для сокращения записи при указании типа итератора.
// Пример 8.3 - автоматический вывод типов при работе с итераторами
std::map<std::string, int> numbers = {
{"One", 1}, {"Two", 2}, {"Three", 3}
};
std::map<std::string, int>::const_iterator it = numbers.begin();
for(; it != numbers.end(); ++it)
std::cout << it->first << " , " << it->second << std::endl;
auto aIt = numbers.begin();
for(; aIt != numbers.end(); ++aIt)
std::cout << aIt->first << " , " << aIt->second << std::endl;
decltype(aIt) aIt2 = numbers.begin();
for(; aIt2 != numbers.end(); ++aIt2)
std::cout << aIt2->first << " , " << aIt2->second << std::endl;
Результат работы программы следующий:
One , 1
Three , 3
Two , 2
One , 1
Three , 3
Two , 2
One , 1
Three , 3
Two , 2
В первом случае тип данных указан явно, во втором случае был использован автоматический вывод типов, в третьем случае было использовано ключевое слово decltype, позволяющее определить тип данных переменной. Как легко убедиться, вывод программы для всех трех случаев идентичный.
Ключевое слово decltype используется для определения типа данных переменной. Оно необходимо, так как автоматически выведенный тип известен только компилятору, и для объявления новой переменной такого же типа используется конструкция вида decltype(other_var) new_var.
8.2 Списки инициализации
В стандарте С++11 вводится специальный класс для работы со списками инициализации, т.е. конструкциями вида {1,2,3,4,5} − std::initializer_list. Данный класс является шаблонным, экземпляры данного класса создаются компилятором статически. Список инициализации является константным и не может быть изменен во время работы программы.
Контейнерам стандартной библиотеки могут быть присвоены изначальные значения при помощи списка инициализации. Далее приведены два примера кода для контейнера std::vector и std::map.
// Пример 8.4 - начальное состояние контейнера std::vector, задаваемое
// с помощью списка инициализации.
std::cout << "Vector\n";
std::vector<int> vec = {1,2,3,4,5,6,7,8,9,10};
for(auto it = vec.begin(); it != vec.end(); ++it)
std::cout << *it << ' ';
std::cout << std::endl;
Результат работы программы следующий:
Vector
1 2 3 4 5 6 7 8 9 10
// Пример 8.5 - начальное состояние контейнера std::map, задаваемое
// с помощью списка инициализации.
std::cout << "Map\n";
std::map<std::string, std::string> m = {
{"Hello", "World"},{"Moscow","Russia"},{"London","GB"}};
for(std::map<std::string, std::string>::iterator it = m.begin(); it != m.end(); ++it)
std::cout << it->first << ", " << it->second << std::endl;
Результат работы программы следующий:
Map
Hello, World
London, GB
Moscow, Russia
Класс может содержать конструктор с параметром − списком инициализации, создание объекта такого класса будет схоже с созданием статического массива. Список инициализации может быть использован и для передачи параметров в функцию. В следующем фрагменте кода показаны обе эти возможности.
// Пример 8.6 - список инициализации в качестве параметра конструктора
class IListConstructed
{
int x, y, z;
public:
IListConstructed(std::initializer_list<int> il) {
x = *(il.begin() + 0);
y = *(il.begin() + 1);
z = *(il.begin() + 2);
}
void print() {
std::cout << "IListConstructed: " << x << y << z << std::endl;
}
void set(std::initializer_list<int> il) {
x = *(il.begin() + 0);
y = *(il.begin() + 1);
z = *(il.begin() + 2);
}
};
IListConstructed ic = {1,2,3};
ic.print();
ic.set({2,3,4});
ic.print();
Результат работы программы следующий:
IListConstructed: 123
IListConstructed: 234
В следующем фрагменте кода показан пример использования списка инициализации для передачи параметров в функцию. Для доступа к элементам списка инициализации используется итератор по аналогии с другими контейнерами стандартной библиотеки.
// Пример 8.7 - список инициализации в качестве параметра функции
int getMax(std::initializer_list<int> iList)
{
if(iList.size() == 0){
std::cout << "Empty initializer list" << std::endl;
return 0;
}
int value = *iList.begin();
for( std::initializer_list<int>::iterator it = iList.begin();
it != iList.end();
++it)
value = value > *it ? value : *it;
return value;
}
std::cout << getMax({3,2,1}) << std::endl;
std::cout << getMax({1,2,3}) << std::endl;
std::cout << getMax({}) << std::endl;
Результат работы программы следующий:
3
3
Empty initializer list
0
