- •И. А. Андрианов, д. В. Кочкин, с. Ю. Ржеуцкая
- •Учебное пособие
- •Оглавление
- •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.1.2 Понятие проекта
Исходный текст программы может находиться в одном или нескольких файлах. Большие программы обычно разбивают на несколько файлов по каким-либо признакам, например, по группам реализуемых функций. Такая группа файлов называется проектом (Project). Многие среды разработки имеют собственные встроенные средства управления проектами и работают со своими файлами конфигурации проекта (.dsp – Microsoft Visual C++, .bpr – Borland C++ Builder и др.)
Использование проекта помогает, например, отслеживать зависимости между файлами при компиляции. Предположим, у нас есть файл consts.h и два использующих его файла – a.cpp и b.cpp. При компиляции будут созданы объектные файлы a.obj и b.obj. Если мы внесём изменения в файл a.cpp, то нам нужно будет заново откомпилировать только его. Однако, если мы изменим consts.h, то необходимо перекомпилировать оба файла – a.cpp и b.cpp. Когда все файлы программы объединены в проект, среда разработки определяет, что требуется перекомпилировать, а что - нет.
Обычно в среде разработки можно выполнить настройки различных параметров компиляции и компоновки проекта – например, включить поддержку стандарта С++11 (если эта опция по умолчанию выключена), добавить внешнюю библиотеку для компоновки и др.
1.1.3 Структура программы на языке С++. Первый пример
С учётом сказанного выше, программа на языке С++ – один или несколько текстовых файлов, которые могут содержать следующие основные структурные элементы:
директивы препроцессора (обычно располагаются в начале файла)
описания переменных и констант
описания типов (классов, шаблонов)
описания функций
комментарии.
Компилятор отличает описания функций от других описаний по наличию круглых скобок, поэтому они обязательны в описании функций, даже если те не содержат списка параметров. Отметим, что в С++ не используется понятие процедуры, вместо него говорят о функции, которая не возвращает результата (в этом случае в качестве типа результата указывается зарезервированное слово void – пустой тип). Любая программа обязательно должна содержать функцию с именем main (или WinMain для графических приложений Windows), которая является точкой входа в программу. Другие функции добавляются в программу при необходимости и вызываются либо из функции main, либо из других функций.
Описание функции традиционно содержит заголовок
тип_результата имя_функции ([список_параметров])
и тело, представляющее собой блок – группу операторов, размещённых между символами- ограничителями { …}. Внутри блока могут располагаться вложенные блоки, при этом должен соблюдаться баланс фигурных скобок. Понятие оператора традиционное – это элементарная инструкция (единица действия) программы. В С++ любой оператор завершается символом ;
Каждая функция обычно содержит оператор (возможно, не один)
return [результат_функции];
который означает немедленный выход из функции и передачу результата в вызывающую функцию. Если тип функции - void, то она или не содержит оператора return или имеет операторы return; (без параметров), которые означают выход из функции без возвращения результата.
Функция main обычно имеет тип int (результат целого типа) и содержит оператор return 0; который в данном случае означает выход из программы и передачу в операционную систему кода завершения 0 – признака успешного завершения программы. Если в функции main оператор return 0; отсутствует, то после выполнения всех операторов код завершения ноль возвращается в операционную систему автоматически.
Подробная информация о функциях представлена в разделах 1.10-1.13.
Описания переменных могут располагаться как вне функций (такие переменные являются глобальными), так и в теле функций (такие переменные локализованы в том блоке, в котором они описаны). Обратим внимание, что переменные, описанные в функции main, являются локальными и не могут быть использованы ("не видны") в других функциях.
Также обратим внимание на чувствительность языка к регистру – N и n являются разными именами.
Для предотвращения конфликтов имён при разработке крупных программных продуктов (одно и то же имя получили два разных объекта) в языке С++ вводится понятие пространства имён (namespace) – каждое имя должно быть уникальным только в пределах своего пространства имён. Все функции и другие объекты стандартной библиотеки С++ определены в пространстве имён std (сокращение от standard), поэтому в начало программы часто помещается директива
using namespace std;
Если этой строки в программе нет, то для обращения к любому идентификатору из стандартной библиотеки требуется добавить к его имени префикс std:: , например, при использовании стандартного потока вывода cout придётся везде указывать std::cout. Такой способ часто применяется (и рекомендуется!) при разработке крупных программных продуктов, использующих много сторонних библиотек со своими пространствами имён. В примерах пособия для сокращения текста часто будет использоваться директива using.
Подробнее пространства имён и их элементы рассматриваются в разделе 1.14.
Комментарии в программе на С++ могут быть:
многострочными /*текст комментария любого размера*/
однострочными //текст комментария до конца текущей строки
Не следует считать комментарии второстепенными элементами программы, не имеющими существенного значения, – они способствуют лучшему пониманию текста программы, следовательно, существенно повышают такой важный показатель качества программного продукта как сопровождаемость. Стоит отметить, что написание комментариев к программе – не менее сложная и ответственная работа, чем разработка самого программного кода.
Пример 1.1. Для примера рассмотрим простую программу на С++, которая вычисляет площадь кольца по заданным значениям внутреннего и внешнего радиусов (r1 и r2 соответственно). Первый пример трудно сделать безупречным – чтобы продемонстрировать использование вспомогательной функции, вычисляющей площадь круга, задача решается "в лоб", т.е. площадь кольца находится как разность площадей большого круга (с радиусом r2) и малого (с радиусом r1). Обратите внимание на определение в программе числа ПИ – в стандарте С++ не определена соответствующая константа. Многие компиляторы поддерживают M_PI в библиотеке cmath, но использование M_PI может ухудшить переносимость программы (ещё одно важное качество программного продукта).
// Пример 1.1 – программа вычисления площади кольца
#include<iostream> // подключение библиотеки ввода/вывода
#include<cstdlib> // для работы system("pause")
using namespace std; //используем стандартное пространство имён std
const double PI = 3.1415926536; //определение ПИ -глобальная константа
double s_circle(double r) { // вычисление площади круга с радиусом r
return PI*r*r;
}
int main() {
//настройка корректного вывода кириллицы
setlocale(LC_ALL,"Russian");
double r1, r2, s;
cout << "Введите внутренний и внешний радиусы "; cin >> r1 >> r2;
s = s_circle(r2) - s_circle(r1);
cout << "S = " << s;
system("pause"); //пауза до нажатия любой клавиши
return 0; // нормальный выход из программы с кодом завершения 0
}
В дальнейших примерах мы не будет пользоваться функциями setlocale и system("pause"). Кириллицу по возможности использовать не будем, а функцию паузы возложим на среду разработки (например, в Visual Studio при запуске программы с помощью комбинации клавиш CTRL-F5 пауза обеспечивается автоматически). При необходимости добавьте вызов system("pause"); в другие примеры.
