- •И. А. Андрианов, д. В. Кочкин, с. Ю. Ржеуцкая
- •Учебное пособие
- •Оглавление
- •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 Методики оптимизации кода
- •Заключение
- •Библиографический список
1.5.2 Циклы
Они служат для реализации многократно повторяющихся действий. Как и в других языках программирования, в С++ имеется три основных типа операторов цикла:
Цикл for
Цикл с предусловием (while)
Цикл с постусловием (do…while)
В стандарте c++ 11 введён ещё один цикл for по диапазону – его синтаксис использует массив, поэтому пример с эти оператором цикла рассмотрим в следующем разделе, посвящённому массивам.
Оператор for в языке С++ представляет собой наиболее общий способ организации цикла, который в программах используется чаще других, поэтому начнём с него. Он имеет следующий формат:
for(выражение1;выражение2;выражение3) тело цикла
Выражение1 обычно используют для установки начального значения переменных, управляющих циклом. Выражение2 – это выражение, определяющее условие, при котором тело цикла будет выполняться. Выражение3 определяет изменения переменных, управляющих циклом, после каждого выполнения тела цикла.
Здесь и далее тело цикла может представлять собой как одиночный оператор, так и блок (составной оператор).
В качестве примера приведём простую короткую программу, которая вычисляет сумму квадратов натуральных чисел от 1 до 9. На этом примере разберём использование различных операторов цикла.
//Пример 1.4 с оператором for – вычисление суммы квадратов от 1 до 9
#include <iostream>
using namespace std;
int main(){
int i, b=0;
for(i=1; i<10; i++)
b += i * i;
cout << b << endl;
system("pause"); return 0;
}
Оператор while - цикл с предусловием.
while (выражение) тело цикла
Пример 1.4 запишем, используя оператор while:
// фрагмент примера 1.4 с использованием while
int i=1, b=0;
while (i<10) {
b += i * i; i++;
}
cout << b << endl;
Оператор do while – цикл с постусловием.
do тело цикла while (выражение);
Тело цикла выполняется хотя бы один раз. Если выражение ложно, выполнение цикла заканчивается, иначе продолжается.
Тот же пример 1.4, но с использованием цикла do….while:
// фрагмент примера 1.4 с использованием do…while
int i=1,b=0;
do {
b+=i*i; i++;
} while(i<10);
cout << b << endl;
Обратим внимание, что во всех циклах пишется не условие окончания, а условие продолжения цикла.
В теле циклов можно использовать операторы break и continue. Оператор break выходит из тела цикла (прерывает цикл), continue передаёт управление на очередную итерацию.
Пример 1.4 c использованием "бесконечного" цикла for и оператора break:
// фрагмент примера 1.4 с использованием breaK
int i=1,b=0;
for(;;) {b+=i*i; i++; if (i==10) break;}
cout << b << endl;
1.6 Массивы, строки
Хороший пример использования в программах ветвлений и циклов – обработка массивов и строк текста.
1.6.1 Основные понятия
Массив – совокупность элементов одинакового типа, объединённых общим именем; для доступа к элементам необходимо указать имя массива и один или несколько индексов, однозначно определяющих порядковый номер элемента в массиве. Количество индексов у элементов массива зависит от размерности массива – элементы одномерного массива (вектора) имеют по одному индексу, элементы двумерного массива (матрицы) – два индекса, можно использовать и массивы большей размерности.
Строка – одномерный массив, элементами которого являются символы.
В языке С++ с массивами и строками можно работать двумя способами:
1. использование стандартных языковых возможностей и встроенных средств компилятора для работы с массивами (встроенные массивы) – этот способ достался языку С++ в наследство от языка С и практически не изменился;
2. использование типов (классов, шаблонов), определённых в стандартной библиотеке С++ специально для обработки массивов и строк. Для работы с массивами различных типов рекомендуется использовать шаблонный класс vector (например, типы vector<int>, vector<double> и т.д.), для работы со строками (символьными массивами) имеется отдельный тип string (или wstring при использовании кодировки Unicode).
Оба способа имеют свои плюсы и минусы.
При использовании стандартной библиотеки программист получает в своё распоряжение множество реализованных в ней типовых алгоритмов обработки массивов и строк. Важно, что в этом случае не требуется решать сложную проблему выделения и освобождения памяти, что особо актуально при работе с массивами, реальные размеры которых на этапе разработки программы предсказать сложно (или даже невозможно – такое бывает не так уж и редко). Понятно, что работа программиста в этом случае существенно упрощается. Обратим внимание, что при этом не только сокращаются сроки разработки программы, но и уменьшается количество ошибок в ней, так как все функции стандартной библиотеки С++ хорошо протестированы и апробированы. Это безусловный плюс.
Но и без минусов не бывает – в силу своей универсальности типы из стандартной библиотеки имеют достаточно сложное внутреннее представление; работа с ними менее эффективна и по расходам памяти, и по быстродействию, чем со встроенными массивами. В ситуациях, когда фактор эффективности имеет первостепенное значение, следует отдать предпочтение встроенным массивам и собственному коду, написанному специально для решения конкретной задачи и учитывающим её особенности. Стоит отметить, что при компиляции проекта в режиме “Release” скорость работы с векторами незначительно уступает скорости работы с встроенными массивами, но в отладочном режиме “Debug”, который установлен по умолчанию, разница может быть существенной.
В данном разделе мы рассмотрим базовые средства языка для работы с массивами и строками (встроенные массивы). В следующем разделе поясним, как можно повысить эффективность обработки массивов и строк, используя указатели, и кратко перечислим возможности библиотеки cstring. Далее коснёмся вопросов использования двух востребованных типов стандартной библиотеки – vector и string.
