После изучения этого примера вы сможете легко выполнять сериализацию и десериализацию сложных структур данных.
Как это делается
В этом примере мы определим другую структуру, как делали в прошлом примере, но на сей раз заполним элементами данной структуры ассоциативный массив, что несколько усложняет задачу, поскольку этот контейнер соотносит ключи и значения вместо размещения всех значений в списке.
1. Сначала включим все необходимые заголовочные файлы и объявим об использовании пространства имен std
#include
#include
#include
#include
#include
#include
using namespace std;
2. Мы хотим создать небольшую базу данных интернет-мемов. Предположим, мем имеет название, описание и год, в который он родился или был создан. Сохраним их в контейнере std::map
struct meme {
string description;
size_t year;
};
3. Сначала проигнорируем ключ и просто реализуем перегруженную функцию потокового оператора >>
meme
. Предположим, что описание мема окружено кавычками, за ним следует год. Это будет выглядеть так: "some description" 2017
. Описание окружено кавычками, так что может содержать пробелы, поскольку мы знаем, что все символы, стоящие между кавычками, принадлежат описанию. После чтения с помощью конструкции is >> quoted(m.description)
кавычки автоматически используются как разделители и впоследствии отбрасываются, что очень удобно. Сразу после этого считываем число, которое представляет собой год:istream& operator>>(istream &is, meme &m) {
return is >> quoted(m.description) >> m.year;
}
4. О’кей, теперь примем в расчет название мема в качестве ключа ассоциативного массива. Чтобы вставить мем в массив, нужно создать объект типа std::pair<тип_ключа, тип_значения>
string
, а типом значения — meme
. В названии мема также могут находиться пробелы, поэтому мы используем ту же оболочку, которую применяли для описания. p.first
— это название, а p.second
— целая структура meme
, связанная с ним. Данный объект будет передан другой реализации оператора >>
, которую мы только что создали:istream& operator >>(istream &is,
pair
return is >> quoted(p.first) >> p.second;
}
5. На этом все. Напишем функцию main
>>
, итератор istream_iterator
может работать с данным типом непосредственно. Мы позволим ему десериализовать наши объекты типа meme
, полученные из стандартного потока ввода, и используем итератор вставки, чтобы поместить их в ассоциативный массив:int main()
{
map
copy(istream_iterator
{},
inserter(m, end(m)));
6. Прежде чем вывести на экран все, что у нас есть, сначала найдем
std::accumulate
. Он получит исходное значение 0u
(u расшифровывается как unsigned — «беззнаковый») и пройдет по всем элементам ассоциативного массива, чтобы accumulate
слияние обычно означает сложение. В нашем случае требуется не численная max_func
, которая принимает переменную, содержащую текущий максимальный размер строки (она должна быть беззнаковой, поскольку длина строки знака не имеет), и сравнивает ее с длиной названия текущего мема, чтобы взять максимальное значение. Это делается для каждого элемента. Итоговое возвращаемое значение функции accumulate
представляет собой максимальную длину названия мема: auto max_func ([](size_t old_max,
const auto &b) {
return max(old_max, b.first.length());
});
size_t width {accumulate(begin(m), end(m),
0u, max_func)};
7. Теперь быстро пройдем по ассоциативному массиву и выведем каждый элемент. Чтобы выходные данные смотрелись более «аккуратно», воспользуемся конструкцией << left << setw(width)
for (const auto &[meme_name, meme_desc] : m) {
const auto &[desc, year] = meme_desc;
cout << left << setw(width) << meme_name
<< " : " << desc
<< ", " << year << '\n';
}
}