Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Serialization - Tutorial

Boost , ,

C++ Boost

Serialization

Tutorial


A Very Simple Case
Non Intrusive Version
Serializable Members
Derived Classes
Pointers
Arrays
STL Collections
Class Versioning
Splitting serialize into save/load
Archives
List of examples
An output archive is similar to an output data stream. Data can be saved to the archive with either the << or the & operator:

ar << data;
ar & data;
An input archive is similar to an input datastream. Data can be loaded from the archive with either the >> or the & operator.

ar >> data;
ar & data;

Когда эти операторы используются для примитивных типов данных, данные просто сохраняются / загружаются в / из архива. При вызове для типов данных класса вызывается функция класса<serialize>. Каждый<serialize>функция использует вышеупомянутых операторов для сохранения/загрузки своих членов данных. Этот процесс будет продолжаться рекурсивным образом до тех пор, пока все данные, содержащиеся в классе, не будут сохранены/загружены.

Очень простой случай

Эти операторы используются внутри функции<serialize>для сохранения и загрузки элементов данных класса.

В эту библиотеку включена программа под названиемdemo.cpp, которая иллюстрирует, как использовать эту систему. Ниже мы выдерживаем код из этой программы, чтобы проиллюстрировать в простейшем случае, как эта библиотека предназначена для использования.<


#include <fstream>
// include headers that implement a archive in simple text format
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
/////////////////////////////////////////////////////////////
// gps coordinate
//
// illustrates serialization for a simple type
//
class gps_position
{
private:
    friend class boost::serialization::access;
    // When the class Archive corresponds to an output archive, the
    // & operator is defined similar to <<.  Likewise, when the class Archive
    // is a type of input archive the & operator is defined similar to >>.
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & degrees;
        ar & minutes;
        ar & seconds;
    }
    int degrees;
    int minutes;
    float seconds;
public:
    gps_position(){};
    gps_position(int d, int m, float s) :
        degrees(d), minutes(m), seconds(s)
    {}
};
int main() {
    // create and open a character archive for output
    std::ofstream ofs("filename");
    // create class instance
    const gps_position g(35, 59, 24.567f);
    // save data to archive
    {
        boost::archive::text_oarchive oa(ofs);
        // write class instance to archive
        oa << g;
    	// archive and stream closed when destructors are called
    }
    // ... some time later restore the class instance to its orginal state
    gps_position newg;
    {
        // create and open an archive for input
        std::ifstream ifs("filename");
        boost::archive::text_iarchive ia(ifs);
        // read class state from archive
        ia >> newg;
        // archive and stream closed when destructors are called
    }
    return 0;
}

>

Для сохранения каждого класса посредством сериализации должна существовать функция сохранения всех членов класса, определяющих состояние класса. Чтобы каждый класс загружался с помощью сериализации, должна существовать функция загрузки членов класса в той же последовательности, в которой они были сохранены. В приведенном выше примере эти функции генерируются функцией члена шаблона<serialize>.

Ненавязчивая версия

Вышеприведенная формулировка является навязчивой. То есть необходимо изменить классы, экземпляры которых должны быть сериализованы. В некоторых случаях это может быть неудобно. Эквивалентная альтернативная формулировка, допускаемая системой:<


#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
class gps_position
{
public:
    int degrees;
    int minutes;
    float seconds;
    gps_position(){};
    gps_position(int d, int m, float s) :
        degrees(d), minutes(m), seconds(s)
    {}
};
namespace boost {
namespace serialization {
template<class Archive>
void serialize(Archive & ar, gps_position & g, const unsigned int version)
{
    ar & g.degrees;
    ar & g.minutes;
    ar & g.seconds;
}
} // namespace serialization
} // namespace boost
>

В этом случае генерируемые сериализованные функции не являются членами класса<gps_position>. Две формулы функционируют точно так же.

Основное применение неинтрузивной сериализации заключается в том, чтобы позволить осуществить сериализацию для классов без изменения определения класса. Чтобы это стало возможным, класс должен предоставить достаточно информации для восстановления классового состояния. В этом примере мы предположили, что в классе было<public>членов — не обычное явление. Только те классы, которые предоставляют достаточно информации для сохранения и восстановления классового состояния, могут быть сериализованы без изменения классового определения.

Сериализируемые члены

Сериализируемый класс с сериализуемыми членами выглядел бы так:<


class bus_stop
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & latitude;
        ar & longitude;
    }
    gps_position latitude;
    gps_position longitude;
protected:
    bus_stop(const gps_position & lat_, const gps_position & long_) :
    latitude(lat_), longitude(long_)
    {}
public:
    bus_stop(){}
    // See item # 14 in Effective C++ by Scott Meyers.
    // re non-virtual destructors in base classes.
    virtual ~bus_stop(){}
};
>

То есть члены классового типа сериализуются так же, как члены примитивных типов.

Обратите внимание, что сохранение экземпляра класса<bus_stop>с одним из операторов архива вызовет функцию<serialize>, которая сохраняет<latitude>и<longitude>. Каждый из них, в свою очередь, будет спасен, взывая<serialize>в определении<gps_position>. Таким образом, вся структура данных сохраняется приложением оператора архива только к корневому элементу.

Производные классы

Производные классы должны включать сериализации своих базовых классов.<


#include <boost/serialization/base_object.hpp>
class bus_stop_corner : public bus_stop
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        // serialize base class information
        ar & boost::serialization::base_object<bus_stop>(*this);
        ar & street1;
        ar & street2;
    }
    std::string street1;
    std::string street2;
    virtual std::string description() const
    {
        return street1 + " and " + street2;
    }
public:
    bus_stop_corner(){}
    bus_stop_corner(const gps_position & lat_, const gps_position & long_,
        const std::string & s1_, const std::string & s2_
    ) :
        bus_stop(lat_, long_), street1(s1_), street2(s2_)
    {}
};

>

Обратите внимание на сериализацию базовых классов из производного класса.НЕнапрямую называйте функции сериализации базового класса. Это может показаться эффективным, но обойдет код, который отслеживает экземпляры, записанные на хранение, чтобы устранить увольнения. Он также обойдет запись информации о версии класса в архив. По этой причине рекомендуется всегда делать функции<serialize>приватными. Декларация<friend boost::serialization::access>предоставляет библиотеке сериализации доступ к частным переменным и функциям членов.

Указатели

Предположим, мы определяем автобусный маршрут как множество автобусных остановок. Учитывая, что
  1. у нас может быть несколько типов автобусных остановок (помните, автобус_стоп является базовым классом)
  2. , данный автобус_стоп может появиться более чем на одном маршруте.
удобно представлять маршрут автобуса с массивом указателей на<bus_stop>.<

class bus_route
{
    friend class boost::serialization::access;
    bus_stop * stops[10];
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        int i;
        for(i = 0; i < 10; ++i)
            ar & stops[i];
    }
public:
    bus_route(){}
};

>Каждый член массива<stops>будет сериализован. Но помните, что каждый член является указателем - так что это может означать? Цель этой сериализации состоит в том, чтобы разрешить реконструкцию исходных структур данных в другом месте и в другое время. Чтобы достичь этого с помощью указателя, недостаточно сохранить значение указателя, а скорее объект, который он указывает, должен быть сохранен. После загрузки элемента необходимо создать новый объект и загрузить новый указатель в класс.

Если один и тот же указатель повторяется более одного раза, в архив добавляется только один экземпляр. При обратном прочтении данные не считываются. Единственная операция, которая происходит для второго указателя, установлена равной первому

. Обратите внимание, что в этом примере массив состоит из полиморфных указателей. То есть каждый элемент массива указывает на один из нескольких возможных видов автобусных остановок. Поэтому, когда указатель сохранен, должен быть сохранен какой-то идентификатор класса. При загрузке указателя должен быть прочитан идентификатор класса и построен экземпляр соответствующего класса. Наконец, данные могут быть загружены во вновь созданный экземпляр правильного типа. Как видно изdemo.cpp, для сериализации указателей на производные классы через указатель базового класа может потребоваться явное перечисление производных классов, подлежащих сериализации. Это называется «регистрация» или «экспорт» производных классов. Это требование и способы его удовлетворения подробно объясняются здесь.

Все это осуществляется автоматически библиотекой сериализации. Приведенный выше код - это все, что необходимо для выполнения сохранения и загрузки объектов, к которым осуществляется доступ через указатели.

Решетки

Вышеприведенная формулировка на самом деле сложнее, чем необходимо. Библиотека сериализации обнаруживает, когда объект, который сериализуется, является массивом и испускает код, эквивалентный вышеупомянутому. Так что вышесказанное можно сократить до:<

class bus_route
{
    friend class boost::serialization::access;
    bus_stop * stops[10];
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & stops;
    }
public:
    bus_route(){}
};

>

.Коллекции STL

В приведенном выше примере используется множество членов. Более вероятно, что такое приложение будет использовать коллекцию STL для такой цели. Библиотека сериализации содержит код для сериализации всех классов STL. Таким образом, приведенная ниже переформулировка также будет работать, как и следовало ожидать.<

#include <boost/serialization/list.hpp>
class bus_route
{
    friend class boost::serialization::access;
    std::list<bus_stop *> stops;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & stops;
    }
public:
    bus_route(){}
};

>

Классовая версия

Предположим, что мы удовлетворены нашим классом<bus_route>, создаем программу, которая использует его и отправляет продукт. Некоторое время спустя было решено, что программа нуждается в улучшении, и класс<bus_route>был изменен, чтобы включить имя водителя маршрута. Так что новая версия выглядит так:<


#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
class bus_route
{
    friend class boost::serialization::access;
    std::list<bus_stop *> stops;
    std::string driver_name;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & driver_name;
        ar & stops;
    }
public:
    bus_route(){}
};

>Отлично, мы все закончили. А как насчет людей, использующих наше приложение, у которых теперь есть куча файлов, созданных в рамках предыдущей программы? Как их можно использовать в новой версии программы?

В общем случае библиотека сериализации хранит номер версии в архиве для каждого сериализованного класса. По умолчанию этот номер версии равен 0. При загрузке архива читается номер версии, под которым он был сохранен. Приведенный выше код может быть изменен для использования<


#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/version.hpp>
class bus_route
{
    friend class boost::serialization::access;
    std::list<bus_stop *> stops;
    std::string driver_name;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        // only save/load driver_name for newer archives
        if(version > 0)
            ar & driver_name;
        ar & stops;
    }
public:
    bus_route(){}
};
BOOST_CLASS_VERSION(bus_route, 1)

>. Применяя версию для каждого класса, нет необходимости пытаться поддерживать версию файлов. То есть файловая версия представляет собой комбинацию версий всех составляющих ее классов. Эта система позволяет программам всегда быть совместимыми с архивами, созданными всеми предыдущими версиями программы, не прилагая больше усилий, чем требуется в этом примере.

Разделение<serialize>на<save/load>

Функция<serialize>проста, лаконична и гарантирует, что члены класса сохраняются и загружаются в одной последовательности — ключ к системе сериализации. Однако есть случаи, когда операции загрузки и сохранения не так похожи, как примеры, используемые здесь. Например, это может произойти с классом, который развился через несколько версий. Вышеприведенный класс можно переформулировать следующим образом:<

#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/split_member.hpp>
class bus_route
{
    friend class boost::serialization::access;
    std::list<bus_stop *> stops;
    std::string driver_name;
    template<class Archive>
    void save(Archive & ar, const unsigned int version) const
    {
        // note, version is always the latest when saving
        ar  & driver_name;
        ar  & stops;
    }
    template<class Archive>
    void load(Archive & ar, const unsigned int version)
    {
        if(version > 0)
            ar & driver_name;
        ar  & stops;
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()
public:
    bus_route(){}
};
BOOST_CLASS_VERSION(bus_route, 1)

>Макро<BOOST_SERIALIZATION_SPLIT_MEMBER()>генерирует код, который вызывает<save>или<load>в зависимости от того, используется ли архив для сохранения или загрузки.

Архивы

Наша дискуссия здесь была сосредоточена на добавлении возможности сериализации в классы. Фактическая визуализация данных, подлежащих сериализации, осуществляется в классе архива. Таким образом, поток сериализованных данных является продуктом сериализации класса и выбранного архива. Ключевым дизайнерским решением является независимость этих двух компонентов. Это позволяет использовать любую спецификацию сериализации с любым архивом.

В этом учебнике мы использовали определенный класс архивов —<text_oarchive>для сохранения и<text_iarchive>для загрузки. Текстовые архивы отображают данные в виде текста и переносятся на различные платформы. В дополнение к текстовым архивам библиотека включает класс архивов для нативных двоичных данных и xml-форматированных данных. Интерфейсы для всех классов архивов идентичны. После того, как для класса определена сериализация, этот класс может быть сериализован для любого типа архива.

Если текущий набор классов архивов не предоставляет атрибуты, формат или поведение, необходимые для конкретного приложения, можно либо создать новый класс архивов, либо вывести из существующего. Это описано позже в руководстве.

Список примеров

demo.cpp
Это полный пример, используемый в этом учебнике. Он делает следующее:
  1. Создает структуру различных видов остановок, маршрутов и расписаний.
  2. Показать его
  3. Сериализирует его на файл под названием «testfile.txt» с одним утверждением
  4. Восстановление в другую структуру
  5. Восстановленная структура
Выход этой программыдостаточен для проверки того, что все первоначально заявленные требования к системе сериализации удовлетворяются этой системой. Содержимоеархивного файлатакже может отображаться, поскольку файлы сериализации являются текстом ASCII.
demo_xml.cpp
Это вариация оригинальной демо-версии, которая поддерживает xml-архивы в дополнение к другим. Дополнительный макрос обертки, BOOST_SERIALIZATION_NVP (имя), необходим для связи имени элемента данных с соответствующим тегом xml. Важно, чтобы имя было действительным тегом xml, иначе восстановить архив будет невозможно. Для получения дополнительной информации см.Пары имён-ценностей.Вот как выглядит архив xml.
demo_xml_save.cppиdemo_xml_load.cpp
Обратите внимание также, что, хотя наши примеры сохраняют и загружают данные программы в архив в рамках одной и той же программы, это просто удобство для целей иллюстрации. Архив может загружаться или не загружаться той же программой, которая его создала.

Проницательный читатель может заметить, что эти примеры содержат тонкий, но важный недостаток. Они пропускают память. Автобусные остановки создаются в функции< main>. Расписание автобусов может относиться к этим автобусным остановкам любое количество раз. По окончании основной функции после разрушения расписания автобусов уничтожаются остановки автобусов. Это кажется нормальным. Но как насчет структуры<new_schedule>элемента данных, созданного процессом загрузки из архива? Это включает в себя отдельный набор автобусных остановок, которые не указаны вне расписания автобусов. Они не будут уничтожены нигде в программе - утечка памяти.

Есть несколько способов исправить это. Одним из способов является явное управление автобусными остановками. Однако более надежным и прозрачным является использование<shared_ptr>, а не сырых указателей. Наряду с реализациями сериализации для Стандартной библиотеки библиотека сериализации включает реализацию сериализации для<boost::shared ptr>. Учитывая это, должно быть легко изменить любой из этих примеров, чтобы устранить утечку памяти. Это остается в качестве упражнения для читателя.


© CopyrightRobert Ramey2002-2004. Распространяется под лицензией Boost Software License, версия 1.0. (См. сопроводительный файл LICENSE_1_0.txt или копию по адресу http://www.boost.org/LICENSE_1_0.txt)

Статья Serialization - Tutorial раздела может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-07-05 08:35:39/0.0077950954437256/0