Основное преимущество бинарных форматов сериализации перед текстовыми состоит в их компактности. Давайте в качестве примера рассмотрим число 5.036.843. Как вы знаете, для представления такого числа в C++ будет использоваться тип int, который занимает четыре байта. То есть бинарное представление этого числа занимает четыре байта, в то время как текстовое представление занимает семь. Потому что в нашем числе семь цифр, соответственно для его текстовой записи нужно семь символов — это как раз семь байт. Так что уже на таком примере мы видим, что бинарный формат гораздо более компактный. Давайте изучим, какие средства работы с бинарной сериализацией нам предоставляет стандартная библиотека C++. На самом деле в стандартной библиотеке не очень-то и много средств для работы с бинарной сериализацией. Стандартный поток ostream предоставляет один-единственный метод write, который принимает буфер байт, собственно указатель на этот буфер и размер, и записывает эти байты в поток. И поток istream имеет метод read, который принимает буфер и размер. И собственно, работа этого метода заключается в том, что происходит чтение из потока в переданный буфер. И давайте для примера воспользуемся этими методами и для начала сериализуем какую-то строку. Я открою IDE, и давайте сделаем строчку, пусть это будет какая-нибудь abracadabra, и мы хотим эту строчку сериализовать в бинарный файл. Создадим выходной поток, файл. Допустим, назовем его data.bin, и сохраним нашу строчку. Передадим указатель на буфер, вызвав метод c_str, и размер нашей строчки. Компилируем. У нас все компилируется. И давайте сразу же выполним чтение из этого файла. Создадим входной файловый поток. И допустим, создадим другую переменную строковую, которая будет иметь необходимый размер. Заполним ее, допустим, пробелами. И собственно, выполним чтение. Прочитаем из входного потока in в dest.data. Это метод, который возвращает неконстантный указатель на буфер строки. И dest.size. И давайте выведем, что у нас считалось. Компилируем, запускаем и видим, что в консоль вывелось abracadabra, так что у нас все работает. Однако сериализация строк в данном случае нам не очень интересна, потому что мы говорили, что основное преимущество бинарный форматов — это компактность, а со строчками ты никакой компактности не добьешься. Поэтому давайте вместо строчки в качестве другого примера рассмотрим сериализацию целого числа. Объявим переменную, возьмем наше число 5'036'843. Я даже поставлю апострофы. Здесь мы пока закомментируем и сохраним наше число в выходной поток. Сделаем мы это таким образом, мы в качестве буфера передадим указатель на наше число, а в качестве размера — размер переменной value. Запускаем компиляцию, и что-то идет не так, наша программа не компилируется. И дело в том, что компилятор пишет, что не знает, как превратить указатель на int в указатель на char. Действительно, эти указатель указывают на разные типы и неявно друг в друга не преобразуются. Поэтому нам надо каким-то образом превратить указатель на int в указатель на char. Пока что мы с вами изучали только один оператор преобразования типов — это static_cast, и давайте мы им здесь воспользуемся. Сделаем static_cast<char*>, попробуем превратить int* в char*. Запускаем компиляцию, и опять у нас компиляция не проходит. И снова нам компилятор говорит, что не может преобразовать указатель на int в указатель на char. Как же нам быть? У нас такая сигнатура метода, что нам надо туда передать указатель на char, как же нам все-таки сериализовать целое число? Вроде бы задача простая. И здесь нам поможет специальный оператор преобразования, который называется reinterpret_cast. Это специальная конструкция языка C++, которая говорит компилятору следующее. Давайте мы сначала скомпилируем. Вот наш код скомпилировался. Она говорит компилятору следующее: возьми вот эту пачку битов, которая является аргументом оператора reinterpret_cast, и забудь о том, какой тип она имеет, а воспринимай эту пачку битов, как будто она имеет тот тип, который передан в угловых скобках. Таким образом, мы, скажем так, приказываем компилятору воспринимать указатель на value как указатель на char. Итак, наша программа скомпилировалась, давайте мы ее запустим, вот она отработала. Теперь давайте откроем файловый менеджер. Вот у меня здесь он уже открыт. Вот файл data.bin, и мы видим, что этот файл имеет размер четыре байта. Мы говорили, что бинарное представление нашего числа умещается в тип int и имеет четыре байта. Вот мы видим, что бинарно int действительно сериализовался в четыре байта. Прекрасно. И теперь давайте мы наше число считаем. Для этого мы сделаем вот как. Мы запишем в переменную value какое-нибудь другое число. Точно так же с помощью reinterpret_cast преобразуем указатель на int в указатель на char. И давайте скомпилируем (здесь у нас должен быть не dest, а value), запустим. И у нас вывелось наше число 5'036'843. Таким образом, мы видим, что бинарная сериализация для целого числа типа int у нас отлично работает. Давайте еще немного поговорим об операторе reinterpret_cast. Дело в том, что он позволяет обмануть компилятор и как бы сломать систему статической типизации, которая применяется в языке C++. И бывает, что начинающие C++ программисты злоупотребляют этим оператором, чтобы заставить свою программу компилироваться. Но мы же с вами получаем черный пояс по C++, поэтому мы пишем не абы какой код, а мы стараемся разрабатывать качественный, надежный код. И поэтому давайте мы с вами откроем документацию по оператору reinterpret_cast. Вот у меня страничка с cppreference про reinterpret_cast. И здесь есть очень важный раздел. Здесь есть вот такая фраза: только ниже перечисленные преобразования могут выполняться с помощью оператора reinterpret_cast. Листаем ниже и видим список нумерованный, и мы видим, что различных разрешенных преобразований всего 11. При этом, если вчитаться в то, что здесь написано, то они весьма специфичны. Мы с вами воспользовались пунктом пять, который гласит, что любой объект, являющийся указателем на тип T1, может с помощью reinterpret_cast быть превращен в указатель на тип T2. То есть с помощью reinterpret_cast безопасно преобразовывать указатели друг в друга. Мы с вами так и сделали, когда превратили указатель на int в указатель на char. Таким образом, reinterpret_cast имеет свою область применения, но она довольно узкая, и поэтому нужно хорошенько знать случаи, когда им стоит пользоваться, и не злоупотреблять. Давайте подведем итоги. Итак, в этом видео мы сказали, что компактность — это главное преимущество бинарных форматов сериализации. При этом мы узнали, что в стандартной библиотеке C++ для бинарной сериализации используются два метода — это ostream: :write и istream: :read. А также мы познакомились с оператором reinterpret_cast, который позволил нам преобразовать указатель на int (ну и вообще говоря, он позволяет преобразовать произвольный указатель) в указатель на char.