• 【BOOST C++ 19 应用库】(4) Boost.Serialization


    一、说明

            Boost.Serialization 有着丰富的内涵,本文就序列化的不同目的,给出详细解释:如归档、指针和引用、对象、封装。文中并给出足够多的示例代码。

    二、 Boost.Serialization概要

    2.1 一般概念

            Boost C++ 的 序列化 库允许将 C++ 应用程序中的对象转换为一个字节序列, 此序列可以被保存,并可在将来恢复对象的时候再次加载。 各种不同的数据格式,包括 XML,只要具有一定规则的数据格式,在序列化后都产生一个字节序列。所有 Boost.Serialization 支持的格式,在某些方面来说都是专有的。 比如 XML 格式不同用来和不是用 C++ Boost.Serialization 库开发的应用程序交换数据。所有以 XML 格式存储的数据适合于从之前存储的数据上恢复同一个 C++ 对象。 XML 格式的唯一优点是序列化的 C++ 对象容易理解,这是很有用的,比如说在调试的时候。

    2.2 序列化的目的 

    1. 代码可移植性 - 仅依赖于 ANSI C++ 设施。
    2. 代码经济——在适当的情况下利用 C++ 的功能,例如 RTTI、模板和多重继承等,使代码更短、更易于使用。
    3. 每个类定义的独立版本控制。也就是说,当类定义更改时,旧文件仍然可以导入到新版本的类中。
    4. 深度指针保存和恢复。也就是说,指针的保存和恢复是保存和恢复所指向的数据。
    5. 正确恢复共享数据的指针。
    6. STL 容器和其他常用模板的序列化。
    7. 数据可移植性——在一个平台上创建的字节流应该可以在任何其他平台上读取。
    8. 类序列化和归档格式的正交规范。也就是说,任何文件格式都应该能够存储任意一组 C++ 数据结构的序列化,而无需更改任何类的序列化。
    9. 非侵入性。允许将序列化应用于未更改的类。也就是说,不要求要序列化的类派生自特定的基类或实现指定的成员函数。为了轻松地将序列化应用于类库中我们不能或不想更改的类,这是必要的。
    10. 存档界面必须足够简单,以便轻松创建新类型的存档。
    11. 归档接口必须足够丰富,以允许创建以有用的方式将序列化数据呈现为 XML 的归档。

    2.3 其他实现场合

            在开始之前,我搜索了当前的实现。我找到了几个。

    • MFC 这个是我非常熟悉的一个。我已经使用它好几年了,发现它非常有用。然而,它未满足要求 1、2、3、6、7 和 9。尽管未满足所有要求,但这是我发现的最有用的实现。事实证明,类版本控制(部分在 MFC 中实现)对于我的应用程序确实是不可或缺的。不可避免的是,运输程序的 1.x 版本需要在文件中存储比最初提供的更多的信息。 MFC 是这些实现中唯一支持此功能的实现 - 尽管仅适用于最派生的类。尽管如此,它仍然比没有好,并且可以完成工作。 MFC 不实现 STL 集合的序列化。尽管对于 MFC 集合来说它是这样做的。
    • CommonC++ 库 [1] 据我所知,这紧密遵循 MFC 实现,但确实解决了一些问题。它是可移植的并创建可移植的档案,但跳过版本控制。它确实支持指针和 STL 集合的正确且完整的恢复。它确实解决了压缩问题,尽管不是以我喜欢的方式。该软件包还将受益于更好的文档。因此它无法解决 2、3、7、8 和 9 的问题。
    • Eternity [2] 这是一个简单的包。它看起来编码得很好,但它确实需要文档和示例。如果不花时间研究源代码,如何使用它并不明显。最新版本确实支持 XML 格式的文件。 3、6、7、8 和 9 均失败。
    • Holub 的实现 [3] 这篇文章首先让我思考我自己对序列化实现的需求。如果你能忽视散文中傲慢的语气,那么它很有趣,值得一读。此实现失败了 2、3、4、5 和 6。
    • s11n [13] 这个库与这个库有着相似的目标。实现的某些方面也相似。截至撰写本文时,似乎:
    • 仅对最新版本的 GCC 保证可移植性(1)。

    该库未明确支持类定义的版本控制(3)。
    它似乎不会自动考虑共享指针(5)。我从文档以及不支持图形结构的序列化的声明中得出了这一结论。
    它与此实现有很多差异 - 也有很多共同点。

    三、归档序列化

    3.1. 归档

            Boost.Serialization 的主要概念是归档。 归档的文件是相当于序列化的 C++ 对象的一个字节流。 对象可以通过序列化添加到归档文件,相应地也可从归档文件中加载。 为了恢复和之前存储相同的 C++ 对象,需假定数据类型是相同的。

    下面看一个简单的例子。

    #include  
    #include  
    
    int main() 
    { 
      boost::archive::text_oarchive oa(std::cout); 
      int i = 1; 
      oa << i; 
    } 
    
    

            Boost.Serialization 提供了多个归档类,如 boost::archive::text_oarchive 类,它定义在 boost/archive/text_oarchive.hpp 文件中。 boost::archive::text_oarchive,可将对象序列化为文本流。 上面的应用程序将 22 serialization::archive 5 1 写出到标准输出流。

            可见, boost::archive::text_oarchive 类型的对象 oa 可以用来像流 (stream) 一样通过 << 来序列化对象。 尽管如此,归档也不能被认为是可以存储任何数据的常规的流。 为了以后恢复数据,必须以相同的顺序使用和先前存储时用的一样的数据类型。 下面的例子序列化和恢复了 int 类型的变量。

    #include  
    #include  
    #include  
    #include  
    
    void save() 
    { 
      std::ofstream file("archiv.txt"); 
      boost::archive::text_oarchive oa(file); 
      int i = 1; 
      oa << i; 
    } 
    
    void load() 
    { 
      std::ifstream file("archiv.txt"); 
      boost::archive::text_iarchive ia(file); 
      int i = 0; 
      ia >> i; 
      std::cout << i << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

    当 boost::archive::text_oarchive 被用来把数据序列化为文本流, boost::archive::text_iarchive 就用来从文本流恢复数据。 为了使用这些类,必须包含 boost/archive/text_iarchive.hpp 头文件。

    归档的构造函数需要一个输入或者输出流作为参数。 流分别用来序列化或恢复数据。 虽然上面的应用程序使用了一个文件流,其他流,如 stringstream 流也是可以的。

    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      int i = 1; 
      oa << i; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      int i = 0; 
      ia >> i; 
      std::cout << i << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

            这个应用程序也向标准输出流写了 1。 然而,与前面的例子相比, 数据却是用 stringstream 流序列化的。

            到目前为止, 原始的数据类型已经被序列化了。 接下来的例子演示如何序列化用户定义类型的对象。

    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age) 
        : age_(age) 
      { 
      } 
    
      int age() const 
      { 
        return age_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & age_; 
      } 
    
      int age_; 
    }; 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      person p(31); 
      oa << p; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      person p; 
      ia >> p; 
      std::cout << p.age() << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

            为了序列化用户定义类型的对话, serialize() 函数必须定义,它在对象序列化或从字节流中恢复是被调用。 由于 serialize () 函数既用来序列化又用来恢复数据, Boost.Serialization 除了 << 和 >> 之外还提供了 & 操作符。如果使用这个操作符,就不再需要在 serialize() 函数中区分是序列化和恢复了。

       serialize () 在对象序列化或恢复时自动调用。它应从来不被明确地调用,所以应生命为私有的。 这样的话, boost::serialization::access 类必须被声明为友元,以允许 Boost.Serialization 能够访问到这个函数。

            有些情况下需要添加 serialize() 函数却不能修改现有的类。 比如,对于来自 C++ 标准库或其他库的类就是这样的。

    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age) 
        : age_(age) 
      { 
      } 
    
      int age() const 
      { 
        return age_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      friend void serialize(Archive &ar, person &p, const unsigned int version); 
    
      int age_; 
    }; 
    
    template  
    void serialize(Archive &ar, person &p, const unsigned int version) 
    { 
      ar & p.age_; 
    } 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      person p(31); 
      oa << p; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      person p; 
      ia >> p; 
      std::cout << p.age() << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

            为了序列化那些不能被修改的数据类型,要定义一个单独的函数 serialize(),如上面的例子所示。 这个函数需要相应的数据类型的引用作为它的第二个参数。

            如果要被序列化的数据类型中含有不能经由公有函数访问的私有属性,事情就变得复杂了。 在这种情况下,该数据列席就需要修改。 在上面应用程序中的 serialize () 函数如果不声明为 friend ,就不能访问 age_ 属性。

            不过还好,Boost.Serialization 为许多C++标准库的类提供了 serialize() 函数。 为了序列化基于 C++ 标准库的类,需要包含额外的头文件。

    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age, const std::string &name) 
        : age_(age), name_(name) 
      { 
      } 
    
      int age() const 
      { 
        return age_; 
      } 
    
      std::string name() const 
      { 
        return name_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      friend void serialize(Archive &ar, person &p, const unsigned int version); 
    
      int age_; 
      std::string name_; 
    }; 
    
    template  
    void serialize(Archive &ar, person &p, const unsigned int version) 
    { 
      ar & p.age_; 
      ar & p.name_; 
    } 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      person p(31, "Boris"); 
      oa << p; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      person p; 
      ia >> p; 
      std::cout << p.age() << std::endl; 
      std::cout << p.name() << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

            这个例子扩展了 person 类,增加了 std::string 类型的名称变量,为了序列化这个属性property, the header file boost/serialization/string.hpp 头文件必须包含,它提供了合适的单独的 serialize () 函数。

            正如前面所提到的, Boost.Serialization 为许多 C++ 标准库类定义了 serialize () 函数。 这些都定义在和 C++ 标准库头文件名称相对应的头文件中。 为了序列化 std::string 类型的对象,必须包含 boost/serialization/string.hpp 头文件。 为了序列化 std::vector 类型的对象,必须包含 boost/serialization/vector.hpp 头文件。 于是在给定的场合中应该包含哪个头文件就显而易见了。

            还有一个 serialize ()函数的参数,到目前为止我们一直忽略没谈到,那就是 version 。 如果归档需要向前兼容,以支持给定应用程序的未来版本,那么这个参数就是有意义的。 接下来的例子考虑到 person 类的归档需要向前兼容。由于 person 的原始版本没有包含任何名称,新版本的 person 应该能够处理不带名称的旧的归档。

    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age, const std::string &name) 
        : age_(age), name_(name) 
      { 
      } 
    
      int age() const 
      { 
        return age_; 
      } 
    
      std::string name() const 
      { 
        return name_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      friend void serialize(Archive &ar, person &p, const unsigned int version); 
    
      int age_; 
      std::string name_; 
    }; 
    
    template  
    void serialize(Archive &ar, person &p, const unsigned int version) 
    { 
      ar & p.age_; 
      if (version > 0) 
        ar & p.name_; 
    } 
    
    BOOST_CLASS_VERSION(person, 1) 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      person p(31, "Boris"); 
      oa << p; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      person p; 
      ia >> p; 
      std::cout << p.age() << std::endl; 
      std::cout << p.name() << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

    BOOST_CLASS_VERSION 宏用来指定类的版本号。 上面例子中 person 类的版本号设置为1。 如果没有使用 BOOST_CLASS_VERSION , 版本号缺省是0。

    版本号存储在归档文件中,因此也就是归档的一部份。 当一个特定类的版本号通过 BOOST_CLASS_VERSION 宏,在序列化时给定时, serialize () 函数的 version 参数被设为给定值存储在归档中。 如果新版本的 person 访问一个包含旧版本序列化对象的归档时, name_ 由于旧版本不含有这个属性而不能恢复。 通过这种机制,Boost.Serialization 提供了向前兼容归档的支持。

    四、指针和引用

    4.1 指针和引用

            Boost.Serialization 还能序列化指针和引用。 由于指针存储对象的地址,序列化对象的地址没有什么意义,而是在序列化指针和引用时,对象的引用被自动地序列化。

    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age) 
        : age_(age) 
      { 
      } 
    
      int age() const 
      { 
        return age_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & age_; 
      } 
    
      int age_; 
    }; 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      person *p = new person(31); 
      oa << p; 
      std::cout << std::hex << p << std::endl; 
      delete p; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      person *p; 
      ia >> p; 
      std::cout << std::hex << p << std::endl; 
      std::cout << p->age() << std::endl; 
      delete p; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

    上面的应用程序创建了一个新的 person 类型的对象,使用 new 创建并赋值给指针 p 。 是指针 - 而不是 *p - 被序列化了。Boost.Serialization 自动地通过 p 的引用序列化对象本身而不是对象的地址。

    如果归档被恢复, p 不必指向相同的地址。 而是创建新对象并将它的地址赋值给 p 。 Boost.Serialization 只保证对象和之前序列化的对象相同,而不是地址相同。

    由于新式的 C++ 在动态分配内存有关的地方使用 智能指针 (smart pointers) , Boost.Serialization 对此也提供了相应的支持。

    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age) 
        : age_(age) 
      { 
      } 
    
      int age() const 
      { 
        return age_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & age_; 
      } 
    
      int age_; 
    }; 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      boost::scoped_ptr p(new person(31)); 
      oa << p; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      boost::scoped_ptr p; 
      ia >> p; 
      std::cout << p->age() << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

    例子中使用了智能指针 boost::scoped_ptr 来管理动态分配的 person 类型的对象。 为了序列化这样的指针,必须包含 boost/serialization/scoped_ptr.hpp 头文件。

    在使用 boost::shared_ptr 类型的智能指针的时候需要序列化,那么必须包含 boost/serialization/shared_ptr.hpp 头文件。

    下面的应用程序使用引用替代了指针。

    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age) 
        : age_(age) 
      { 
      } 
    
      int age() const 
      { 
        return age_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & age_; 
      } 
    
      int age_; 
    }; 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      person p(31); 
      person &pp = p; 
      oa << pp; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      person p; 
      person &pp = p; 
      ia >> pp; 
      std::cout << pp.age() << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

    可见,Boost.Serialization 还能没有任何问题地序列化引用。 就像指针一样,引用对象被自动地序列化。

    五、对象类层次结构的序列化

            为了序列化基于类层次结构的对象,子类必须在 serialize ()函数中访问 boost::serialization::base_object ()。 此函数确保继承自基类的属性也能正确地序列化。         下面的例子演示了一个名为 developer 类,它继承自类 person 

    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age) 
        : age_(age) 
      { 
      } 
    
      int age() const 
      { 
        return age_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & age_; 
      } 
    
      int age_; 
    }; 
    
    class developer 
      : public person 
    { 
    public: 
      developer() 
      { 
      } 
    
      developer(int age, const std::string &language) 
        : person(age), language_(language) 
      { 
      } 
    
      std::string language() const 
      { 
        return language_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & boost::serialization::base_object(*this); 
        ar & language_; 
      } 
    
      std::string language_; 
    }; 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      developer d(31, "C++"); 
      oa << d; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      developer d; 
      ia >> d; 
      std::cout << d.age() << std::endl; 
      std::cout << d.language() << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

       person 和 developer 这两个类都包含有一个私有的 serialize () 函数, 它使得基于其他类的对象能被序列化。 由于 developer 类继承自 person 类, 所以它的 serialize () 函数必须确保继承自 person 属性也能被序列化。

            继承自基类的属性被序列化是通过在子类的 serialize () 函数中用 boost::serialization::base_object () 函数访问基类实现的。 在例子中强制要求使用这个函数而不是 static_cast 是因为只有 boost::serialization::base_object () 才能保证正确地序列化。

            动态创建对象的地址可以被赋值给对应的基类类型的指针。 下面的例子演示了 Boost.Serialization 还能够正确地序列化它们。

    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age) 
        : age_(age) 
      { 
      } 
    
      virtual int age() const 
      { 
        return age_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & age_; 
      } 
    
      int age_; 
    }; 
    
    class developer 
      : public person 
    { 
    public: 
      developer() 
      { 
      } 
    
      developer(int age, const std::string &language) 
        : person(age), language_(language) 
      { 
      } 
    
      std::string language() const 
      { 
        return language_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & boost::serialization::base_object(*this); 
        ar & language_; 
      } 
    
      std::string language_; 
    }; 
    
    BOOST_CLASS_EXPORT(developer) 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      person *p = new developer(31, "C++"); 
      oa << p; 
      delete p; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      person *p; 
      ia >> p; 
      std::cout << p->age() << std::endl; 
      delete p; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

            应用程序在 save () 函数创建了 developer 类型的对象并赋值给 person* 类型的指针,接下来通过 << 序列化。

            正如在前面章节中提到的, 引用对象被自动地序列化。 为了让 Boost.Serialization 识别将要序列化的 developer 类型的对象,即使指针是 person* 类型的对象。 developer 类需要相应的声明。 这是通过这个 BOOST_CLASS_EXPORT 宏实现的,它定义在 boost/serialization/export.hpp 文件中。 因为 developer 这个数据类型没有指针形式的定义,所以 Boost.Serialization 没有这个宏就不能正确地序列化 developer 

            如果子类对象需要通过基类的指针序列化,那么 BOOST_CLASS_EXPORT 宏必须要用。

            由于静态注册的原因, BOOST_CLASS_EXPORT 的一个缺点是可能有些注册的类最后是不需要序列化的。 Boost.Serialization 为这种情况提供一种解决方案。

    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    class person 
    { 
    public: 
      person() 
      { 
      } 
    
      person(int age) 
        : age_(age) 
      { 
      } 
    
      virtual int age() const 
      { 
        return age_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & age_; 
      } 
    
      int age_; 
    }; 
    
    class developer 
      : public person 
    { 
    public: 
      developer() 
      { 
      } 
    
      developer(int age, const std::string &language) 
        : person(age), language_(language) 
      { 
      } 
    
      std::string language() const 
      { 
        return language_; 
      } 
    
    private: 
      friend class boost::serialization::access; 
    
      template  
      void serialize(Archive &ar, const unsigned int version) 
      { 
        ar & boost::serialization::base_object(*this); 
        ar & language_; 
      } 
    
      std::string language_; 
    }; 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      oa.register_type(); 
      person *p = new developer(31, "C++"); 
      oa << p; 
      delete p; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      ia.register_type(); 
      person *p; 
      ia >> p; 
      std::cout << p->age() << std::endl; 
      delete p; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

            上面的应用程序没有使用 BOOST_CLASS_EXPORT 宏,而是调用了 register_type () 模板函数。 需要注册的类型作为模板参数传入。

            请注意 register_type () 必须在 save () 和 load () 都要调用。

       register_type () 的优点是只有需要序列化的类才注册。 比如在开发一个库时,你不知道开发人员将来要序列化哪些类。 当然 BOOST_CLASS_EXPORT 宏用起来简单,可它却可能注册那些不需要序列化的类型。

    六、 优化用封装函数

            在理解了如何序列化对象之后,本节介绍用来优化序列化过程的封装函数。 通过这个函数,对象被打上标记允许 Boost.Serialization 使用一些优化技术。

    下面例子使用不带封装函数的 Boost.Serialization 。

    #include  
    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      boost::array a = { 0, 1, 2 }; 
      oa << a; 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      boost::array a; 
      ia >> a; 
      std::cout << a[0] << ", " << a[1] << ", " << a[2] << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

    上面的应用程序创建一个文本流 22 serialization::archive 5 0 0 3 0 1 2 并将其写到标准输出流中。 使用封装函数 boost::serialization::make_array () ,输出可以缩短到 22 serialization::archive 5 0 1 2 。

    #include  
    #include  
    #include  
    #include  
    #include  
    #include  
    
    std::stringstream ss; 
    
    void save() 
    { 
      boost::archive::text_oarchive oa(ss); 
      boost::array a = { 0, 1, 2 }; 
      oa << boost::serialization::make_array(a.data(), a.size()); 
    } 
    
    void load() 
    { 
      boost::archive::text_iarchive ia(ss); 
      boost::array a; 
      ia >> boost::serialization::make_array(a.data(), a.size()); 
      std::cout << a[0] << ", " << a[1] << ", " << a[2] << std::endl; 
    } 
    
    int main() 
    { 
      save(); 
      load(); 
    } 
    
    

    boost::serialization::make_array () 函数需要地址和数组的长度。 由于长度是硬编码的,所以它不需要作为 boost::array 类型的一部分序列化。任何时候,如果 boost::array 或 std::vector 包含一个可以直接序列化的数组,都可以使用这个函数。 其他一般需要序列化的属性不能被序列化。

    另一个 Boost.Serialization 提供的封装函数是 boost::serialization::make_binary_object () 。 与 boost::serialization::make_array () 类似,它也需要地址和长度。 boost::serialization::make_binary_object () 函数只是为了用来序列化没有底层结构的二进制数据,而 boost::serialization::make_array () 是用来序列化数组的。

    七、后记

            关于序列化,本文从各种应用目的出发,详细地阐述其用法,期望读者在真正需要序列化的时候,能够查阅和快速实现。谢谢阅读!

  • 相关阅读:
    【论文+代码】1706.Transformer简易学习笔记
    nginx端口映射后,跳转带的是内网端口而不是内网端口
    Google Hacking
    3.4 C++高级编程_函数模板_重载
    Zookeeper初步
    leetcode:850. 矩形面积 II【扫描线 + 可重叠的累计长度差分数组】
    JS 数组操作的利器:splice() 和 slice() 方法详解
    matlab 图像均值滤波
    商业化广告--体系学习--5--广告产品存在那些共性和区别
    柔性数组 +结构体中数组名与指针的区别
  • 原文地址:https://blog.csdn.net/gongdiwudu/article/details/128111942