C++ ч.3 Потоки
Потоки
Пользовательский интерфейс библиотеки находится в файле <iostream.h>
Основная задача потоковых средств ввода-вывода - это процесс преобразования объектов определённого типа в последовательность символов и наоборот.
Вывод:
Строгую типовую и единообразную работу, как со встроенными, так и с пользовательскими типами можно обеспечить, если использовать единственное перегруженное имя функции для различных операций вывода.
put(cerr, "x = ") // cerr - выходной поток ошибок. put(cerr, x) /* или cerr << "x = " << x << '\n'; put(cerr, '\n') * cerr стандартный поток ошибок */
Ввод:
Ввод во много сходен с выводом. Есть клас iostream, который реализует операцию ввода >> ("ввести из" - "input from") для небольшого набора стандартных типов. Для пользовательских типов можно определить функцию operator >>.
Состояние потока:
С каждым потоком(istream или ostream) связано определённое состояние. Нестандартные ситуации и ошибки обрабатываются с помощью проверки и установки состояния подходящим образом. Узнать состояние потока, можно с помощью операций над классом ios.
class ios { //... public: int eof() const; // дошли до конца файла int fail() const; // следующая операция будет неудачна int bad() const; // поток испорчен int good() const; // следующая операция будет успешна }
Последняя операция ввода считается успешной, если состояние задаётся
good( ) или eof( ).
Связывание потоков:
Функция tie( ) может установить и разорвать связь между ostream и istream.
Поля ввода:
Функция width( ) устанавливает минимальное число символов, использующееся в последующей операции вывода числа или строки.
Заполнение поля данными символами или выравнивание можно установить с помощью fill( ).
Состояние формата:
В классе ios содержится состояние формата, которое управляется функциями flags( ) и setf( ).
С помощью setf( ) можно управлять расположением символов в пределах поля:
cout.setf(ios::left, ios::adjustfield); // влево cout.setf(ios::right, ios::adjustfield); // вправо
Динамические структуры данных.
Односвязный список:
Элемент односвязного списка это класс, который содержит два поля: 1 адрес в памяти и 2 информация.
Элементы односвязного списка находятся в памяти хаотично. Это означает, что для работы односвязного списка не нужна непрерывная область памяти. Каждый элемент знает чёткий адрес следующего элемента, который идёт за ним.
Двусвязный список:
Содержит три поля: 1 pNext указатель на следующий элемент, 2 pPrew указатель на предыдущий элемент списка и 3 данные.
Бинарный список:
Состоит из узлов, где каждый узел является родителем двух других узлов. Самый первый элемент у которого нет родителей называет корневым. Это упорядоченная структура данных. Листья - это те элементы у которых нет потомков.
Стек:
Динамическая структура данных, состоящая из элементов, но имеющая определённую организацию, согласно которой эти элементы могут попадать в эту коллекцию и извлекаются из неё.
Дек:
Это динамическая структура данных, у которой реализован функционал добавления и удаления элементов с двух сторон.
range-based циклы
for each - предназначен для того, чтобы итерироваться по каким-либо контейнерам и получать доступ к элементам этого контейнера.
for(auto element: коллекция) { // range-based //.... }
Set и Multiset
Упорядоченные ассоциативные контейнеры. Реализованы на основе бинарного дерева.
#include <set> // вызов set и multiset
Отличе set и multiset, что set не хранит повторяющихся элементов.
Оба не могут изменить объект, который находится внутри.
имя_set.insert() // добавляет значение в контейнере имя_set.find() // поиск имя_set.end() // возвращает элемент который находится после коллекции. имя_set,erase() // удаляет элементы из коллекции имя_multiset.lower_bound // поиск первого повторения имя_multiset.upper_bound // поиск последнего повторения
Map и Multimap
Упорядочены, структура бинарного дерева. Хранят пары: ключ и значение.
map - хранит только уникальные значения.
pair - шаблонная пара.
pair<тип> имя; map<тип> имя;
Проектирование и развитие
Ключевая задача проектирования состоит в определении доступной и защищённой частей интерфейса класса, исходя из которых определяется различные части программы.
Проектирование компонентов
- Определить понятие/класс, и установить связь между ними.
- Уточнить определение классов, указав набор операций для каждого:
- Провести классификацию операций. Частности уточнить необходимость построения копирования и уничтожения.
- Убедиться в минимальности, полноте и удобстве.
- Уточнить определение классов, указав их зависимость от других классов:
- Наследование
- Использование зависимостей
- Определить интерфейсы классов:
- Поделить функции.
- Определить точный тип операций класса.
Полезно классифицировать операции класса по тому, как они работают с внутренним состоянием объекта:
- Базовые операции: конструкторы, деструкторы, операции копирования.
- Селекторы: операции, не использующие состояние объекта.
- Модификаторы: операции, изменяющие состояние объекта.
- Операции преобразований: порождают объект другого типа, исходя из значений объекта, к которому они применяются.
- Повторители: операции, которые открывают доступ к объектам класса или используют последовательность объектов.
Лямбда выражения:
Безымянные функции
[захват контекста] (спецификация_параметров) {действие};
int main { int a = 55; auto f = [a или &a]() mutable } cout << a <<endl; // Только чтения (а) а = 100; // (&а) изменяет внешний объект а = 200; // добавление mutable копирует и изменяет внешний объект }; auto q = f() }
Лямбду надо всегда вызывать или передавать, как переменную вызова другой функции.
Явное определение типа:
auto f = []()->int {};