[БЕЗ_ЗВУКА] Перед тем как мы перейдем к части G, давайте я напомню вам, как выглядело решение части E, на котором вы, наверное, будете основывать свое решение. Итак, проще всего начать с функции main, конечно. Вот функция main в решении части E, в авторском решении, выглядела так. Мы берем, читаем входной json. Во входном json мы берем ключ base_requests — это запрос на создание базы, это все массив. Вызываем от него функцию ReadDescriptions и получаем набор остановок и автобусов. Параллельно мы берем routing_settings — настройки маршрутизации, и это все отправляем в конструктор класса TransportCatalog, который в себе агрегирует возможность делать все что угодно с транспортным справочником. По сути это он и есть. Дальше мы берем stat_requests — запросы к самой базе, и вызываем функцию ProcessAll от самой этой базы — от TransportCatalog. И обрабатываем все эти запросы к базе и печатаем итоговый json. Вот так выглядит функция main. Если чуть копнуть в детали, то ReadDescriptions возвращает вектор объектов типа InputQuery, а InputQuery, если посмотреть descriptions.h, это variant из Stop и Bus. А Stop и Bus — это простые структуры. Остановка — это название, координата и расстояние до соседних, а автобус — это название и набор остановок. То есть это простые структуры, а InputQuery — это variant из этих простых структур. И именно это возвращает функция ReadDescriptions. И именно это передается в TransportCatalog. Сам TransportCatalog, если мы в него посмотрим, в конструкте действительно принимает вектор этих самых InputQuery, вот он, вот здесь. И json словарь routing_settings. Внутри себя TransportCatalog хранит на самом деле не очень много: он хранит словарь остановок из названия Сам объект остановки; и хранит словарь автобусов из названия Сам объект автобусы. И помимо этого, поскольку ему нужно уметь строить маршруты, он хранит unique_ptr на объект TransportRouter'а. Объект TransportRouter'а — это на самом деле некоторая прокси, некоторый вспомогательный класс, для того чтобы работать с абстрактными универсальными классами графа и маршрутизатора, Graph :: Router. Почему нам нужна некоторая прокси, почему нужен некоторый класс прослойка? Потому что, во-первых, нужно создать граф. Вот здесь мы создадим некоторый BusGraph, который на самом деле просто DirectedWeightedGraph <double> — вещественные веса ребер. То есть, во-первых, здесь хранится логика создания графа по автобусам и остановкам — это та самая интересная логика, с которой вы намучились в части E. А с другой стороны, здесь хранится логика преобразования абстрактного маршрута, построенного универсальным маршрутизатором, в маршрут в терминах автобусов и остановок. То есть мы должны подождать, потом поехать, потом еще подождать — это все делается именно здесь. И в этом классе в конечном счете хранится что? Здесь хранятся настройки маршрутизации, здесь хранится непосредственно сам граф, здесь хранится unique_ptr на Router, здесь хранится информация про вершины и ребра. То есть вот эта вершина — это... Что у нас известно про вершины? Про вершины... Это просто названия остановок. Да, остановкам соответствуют вершины, поэтому мы просто храним для каждой вершины, а что это за остановка. Для ребер мы храним: либо это ребро ожидания, либо это ребро автобуса. Здесь это все хранится, и здесь есть также обратный маппинг из названий остановок в номера вершин. И в общем-то, все. Когда мы хотим, например, вот здесь построить маршрут, то здесь дальше вызывается уже метод FindRoute у Router. Возвращаемся в функцию main и здесь Requests::ProcessAll делает что? Он в конечном счете вызывает std visit, где для каждого request вызывает метод Process. В итоге составляется json, этот json печатается. Вот такое вот решение. Вопрос из зала вижу: зачем мы иногда в классах некоторые поля храним в unique pointer'ах? Например, в TransportRouter Graph хранится по значению, а сам Router хранится unique pointer'ом. Почему так? Если на самом деле вы захотите здесь положить сам Router, как он есть, давайте я так и сделаю. Вот так. Вам, во-первых, придется поменять конструктор TransportRouter'а, а здесь мы писали router_ = std : : make_unique<Router> (graph_). Наверное, вы тогда захотите написать так: router_ = Router (graph_). И может показаться, что мы решили все наши проблемы. Ну, конечно, нет. Если мы попробуем этот код скомпилировать, то что мы увидим? Мы увидим, что класс роутера оператор равно не существует, потому что какие-то проблемы с классом. Да, use удалит этот function graph оператор равно. На самом деле есть еще одна проблема, про которую компилятор здесь умолчал, она в следующем состоит. Дело в том, что когда мы инициализируем TransportRouter, мы здесь ничего не сказали про то, чем мы инициализируем сам Router. И поэтому от него пытается вызваться конструктор по умолчанию. Но Router на самом деле специфичен тем, что в нем хранится ссылка на Graph. И соответственно у него нет конструктора по умолчанию, потому что мы не можем никак по умолчанию проинициализировать Graph. И у него нет оператора присваивания, потому что мы не можем присвоить какое-то новое значение какой-то ссылке. Поэтому нам приходится использовать unique pointer на Router. Давайте все вернем, как было. Это первая причина хранить что-то unique pointer'ом, а не непосредственно самим значением. Вторая история, которая здесь есть: это хранение в самом TransportCatalog Router'а по unique pointer'у, уже TransportRouter'а. Зачем это нужно здесь? Дело в том, что в TransportRouter'е у нас есть Graph и Router, а в Router'е есть ссылка на этот Graph, как мы помним, еще раз. В общем Router'е... Давайте я вот этот код закрою, чтобы он не мешался. В общем Router'е есть ссылка на Graph. Этот Graph хранится где в нашей программе? Он хранится в TransportRouter'е. Вот у нас класс TransportRouter'а, в нем хранится непосредственно сам Graph, и Router с помощью unique pointer'. То есть по сути этот Router, который внутри вот здесь, он внутри себя ссылается на этот объект Graph'а. И вот здесь нас подстерегает опасность. Если этот объект TransportRouter'а, в котором вот это все происходит, вот это все, если он куда-то уедет, переместится куда-то, то и Graph внутри него, вот это поле graph_ внутри него, тоже переместится и будет иметь другой адрес. И ссылка из Router'а туда по сути инвалидируется. И поэтому вот этот самый TransportRouter, который хранит Graph у себя внутри по значению, нельзя никуда перемещать. Поэтому он должен создаться один раз и там и лежать. Поэтому мы его создаем с помощью make_unique и храним с помощью unique pointer. Так он создался в динамической памяти и никуда не денется. Еще, правда, небольшой нюанс, который вот здесь не очень важен, но если мы говорим про наше светлое будущее, если вы будете писать какие-то библиотеки и пытаться делать удобно пользователям вашего кода, то стоит обсудить такой момент, что сейчас, когда вы используете этот класс TransportRouter'а, вам приходится держать в уме, что его нельзя положить по значению, иначе что-то не то случится со ссылками. И правда, вы можете попробовать вот здесь хранить TransportRouter по значению и найти проблему с помощью того же самого санитайзера. Так вот, пользователям класса TransportRouter приходится помнить о том, что его нужно складывать где-то там в динамической памяти и хранить на него unique_pointer. Нельзя ли сделать так, чтобы это было более прозрачно для пользователя TransportRouter'а? Нельзя ли эти заботы, это бремя переложить на сам класс TransportRouter'а? На самом деле можно. Если мы говорим, что проблема в том, что Graph хранится непосредственно здесь и при перемещении будет куда-то уезжать и ссылка на него будет инвалидироваться, мы можем об этом подумать в самом классе TransportRouter. И для этого можем вот здесь сделать unique pointer на Graph. Тогда Graph не будет никуда уезжать, ссылка на него не будет инвалидироваться, и здесь это будет выглядеть странновато. Зато использование TransportRouter будет абсолютно прозрачным. Итак, вот мы обсудили решение части E. Давайте я еще раз кратко напомню его структуру. У нас есть общий класс базы данных, который умеет все, что нам нужно. Он умеет строиться по набору остановок, набору автобусов, который считан с помощью ReadDescriptions. И умеет отвечать на запросы, которые мы считали из ключа с помощью stat_requests. Так выглядит решение части E.