Программирование
July 20, 2019

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<тип> имя;

Проектирование и развитие

Ключевая задача проектирования состоит в определении доступной и защищённой частей интерфейса класса, исходя из которых определяется различные части программы.

Проектирование компонентов

  • Определить понятие/класс, и установить связь между ними.
  • Уточнить определение классов, указав набор операций для каждого:
  1. Провести классификацию операций. Частности уточнить необходимость построения копирования и уничтожения.
  2. Убедиться в минимальности, полноте и удобстве.
  • Уточнить определение классов, указав их зависимость от других классов:
  1. Наследование
  2. Использование зависимостей
  • Определить интерфейсы классов:
  1. Поделить функции.
  2. Определить точный тип операций класса.

Полезно классифицировать операции класса по тому, как они работают с внутренним состоянием объекта:

  1. Базовые операции: конструкторы, деструкторы, операции копирования.
  2. Селекторы: операции, не использующие состояние объекта.
  3. Модификаторы: операции, изменяющие состояние объекта.
  4. Операции преобразований: порождают объект другого типа, исходя из значений объекта, к которому они применяются.
  5. Повторители: операции, которые открывают доступ к объектам класса или используют последовательность объектов.

Лямбда выражения:

Безымянные функции

[захват контекста] (спецификация_параметров) {действие};
int main
{
    int a = 55;
    auto f = [a или &a]() mutable
    }
        cout << a <<endl; // Только чтения (а)
        а = 100; // (&а) изменяет внешний объект
        а = 200; // добавление mutable копирует и изменяет внешний объект
    };
    auto q = f()
}

Лямбду надо всегда вызывать или передавать, как переменную вызова другой функции.

Явное определение типа:

auto f = []()->int {};