- •И. А. Андрианов, д. В. Кочкин, с. Ю. Ржеуцкая
- •Учебное пособие
- •Оглавление
- •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 Методики оптимизации кода
- •Заключение
- •Библиографический список
1.2.3 Внутреннее представление простых типов
В современных компьютерных архитектурах имеется две основных формы представления данных:
с фиксированной точкой (в русском переводе иногда употребляют термин "с фиксированной запятой", т. к. речь идет о разделителе, отделяющем целую часть числа от дробной части);
с плавающей точкой (запятой).
Первый способ употребляется для хранения всех целочисленных типов, второй способ используется для хранения вещественных чисел.
В представлении с фиксированной точкой целое число хранится в двоичной системе счисления, занимая столько байт памяти, сколько определено для его типа.
Например, целое число 19 в двоичной форме с фиксированной точкой будет записано как 10011 (разложение по степеням двойки: 19=1×24+0×23+0×22+1×21+1×20). В типах signed один крайний бит отводится под знак (0 для положительного числа, 1 для отрицательного). Остальные незанятые биты заполняются нулями.
Зная размер целого типа данных, несложно определить диапазон его допустимых значений: если тип занимает k бит, то количество различных значений равно 2k. При этом для знаковых типов количество отрицательных значений равно количеству неотрицательных – например, для типа int, который обычно занимает 32 бита, диапазон значений получается от -231 до 231-1, то есть от -2147483648 до 2147483647 (на практике обычно достаточно помнить, что диапазон составляет от минус двух миллиардов с небольшим до плюс двух миллиардов с небольшим). Впрочем, точные значения границ диапазонов всегда можно получить с помощью констант, определенных в заголовочном файле climits, например, INT_MIN и INT_MAX.
Представление с плавающей точкой в различных компьютерных архитектурах может быть реализовано по-разному, но в любом случае число рассматривается как произведение двух сомножителей, один из которых представляет собой число 2 в целой степени. Степень двойки определяет порядок числа, а первый из сомножителей определяет значащие цифры числа и называется мантиссой.
Например:
вещественное число 15,375 в двоичной форме с плавающей запятой можно представить как 1,111011×211, где 1,111011 — мантисса, 11 — порядок.
В ячейке памяти, отводимой под число с плавающей точкой, в действительности хранится не одно, а два значения — мантисса и порядок.
Сравнение способов представления с фиксированной и плавающей точкой. Рассмотрим следствия, которые вытекают из рассмотренных способов хранения данных.
Целочисленные типы — это интервал целых чисел. Операции над ними определены лишь тогда, когда исходные данные и результат лежат в этом интервале. Иначе возникает ситуация, называемая переполнением. При целочисленном переполнении, как правило, не возникает ошибка выполнения программы, однако результат становится неверным. За исключением переполнения все операции над аргументами с фиксированной точкой выполняются точно (без погрешности).
Все операции над данными с фиксированной точкой выполняются быстрее, чем над данными с плавающей точкой. В последнем случае значительное время тратится на операции выравнивания порядков (при выполнении операции операнды должны иметь одинаковые порядки).
Вещественные числа, как и целые, представляются конечным множеством значений (хотя количество различных значений вещественных чисел так велико, что может показаться бесконечным).
Каждое вещественное число будет иметь приблизительно одинаковое количество значащих цифр в его представлении, т. к. под мантиссу отводится фиксированное количество байтов. Как следствие этого, ошибка для очень больших чисел будет больше по абсолютной величине.
Вещественные числа всегда представлены с некоторой погрешностью. Все операции над вещественными числами также выполняются с погрешностью. Поэтому нельзя сравнивать на равенство/неравенство вещественные числа обычным способом. Вместо этого можно сравнить модуль разности этих чисел с каким-нибудь достаточно маленьким числом (зависит от решаемой задачи).
Следует быть осторожным при выполнении операций между числами, порядки которых сильно отличаются, например, между очень большими и очень маленькими числами. Возможны ситуации, когда мантисса не сможет обеспечить требуемую точность. Например, при сложении чисел 10000000 и 0.00000001 типа float разрядности мантиссы не хватит, чтобы представить число 10000000.00000001, и результат останется равным 10000000.
Стоит запомнить, что наиболее часто используемый тип double способен хранить 15 значащих десятичных цифр (хотя вследствие погрешностей вычислений точность последних цифр не всегда гарантируется).
Для уменьшения влияния ошибки округления при выполнении арифметических операций с вещественными числами необходимо иметь в виду следующее. Если складывается много чисел, то их необходимо разбить на группы чисел, близких по абсолютному значению, произвести суммирование в группах, начиная с меньшего числа, после чего полученные суммы сложить, опять-таки начиная с меньшей. Таким образом, при работе с вещественными числами при перестановке мест слагаемых сумма может измениться. Похожие рекомендации можно дать и для других арифметических операций.
