11.2. 归档

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

下面看一个简单的例子。

  1. #include <boost/archive/text_oarchive.hpp>
  2. #include <iostream>
  3.  
  4. int main()
  5. {
  6. boost::archive::text_oarchive oa(std::cout);
  7. int i = 1;
  8. oa << i;
  9. }
  10.  

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

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

  1. #include <boost/archive/text_oarchive.hpp>
  2. #include <boost/archive/text_iarchive.hpp>
  3. #include <iostream>
  4. #include <fstream>
  5.  
  6. void save()
  7. {
  8. std::ofstream file("archiv.txt");
  9. boost::archive::text_oarchive oa(file);
  10. int i = 1;
  11. oa << i;
  12. }
  13.  
  14. void load()
  15. {
  16. std::ifstream file("archiv.txt");
  17. boost::archive::text_iarchive ia(file);
  18. int i = 0;
  19. ia >> i;
  20. std::cout << i << std::endl;
  21. }
  22.  
  23. int main()
  24. {
  25. save();
  26. load();
  27. }
  28.  

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

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

  1. #include <boost/archive/text_oarchive.hpp>
  2. #include <boost/archive/text_iarchive.hpp>
  3. #include <iostream>
  4. #include <sstream>
  5.  
  6. std::stringstream ss;
  7.  
  8. void save()
  9. {
  10. boost::archive::text_oarchive oa(ss);
  11. int i = 1;
  12. oa << i;
  13. }
  14.  
  15. void load()
  16. {
  17. boost::archive::text_iarchive ia(ss);
  18. int i = 0;
  19. ia >> i;
  20. std::cout << i << std::endl;
  21. }
  22.  
  23. int main()
  24. {
  25. save();
  26. load();
  27. }
  28.  

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

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

  1. #include <boost/archive/text_oarchive.hpp>
  2. #include <boost/archive/text_iarchive.hpp>
  3. #include <iostream>
  4. #include <sstream>
  5.  
  6. std::stringstream ss;
  7.  
  8. class person
  9. {
  10. public:
  11. person()
  12. {
  13. }
  14.  
  15. person(int age)
  16. : age_(age)
  17. {
  18. }
  19.  
  20. int age() const
  21. {
  22. return age_;
  23. }
  24.  
  25. private:
  26. friend class boost::serialization::access;
  27.  
  28. template <typename Archive>
  29. void serialize(Archive &ar, const unsigned int version)
  30. {
  31. ar & age_;
  32. }
  33.  
  34. int age_;
  35. };
  36.  
  37. void save()
  38. {
  39. boost::archive::text_oarchive oa(ss);
  40. person p(31);
  41. oa << p;
  42. }
  43.  
  44. void load()
  45. {
  46. boost::archive::text_iarchive ia(ss);
  47. person p;
  48. ia >> p;
  49. std::cout << p.age() << std::endl;
  50. }
  51.  
  52. int main()
  53. {
  54. save();
  55. load();
  56. }
  57.  

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

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

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

  1. #include <boost/archive/text_oarchive.hpp>
  2. #include <boost/archive/text_iarchive.hpp>
  3. #include <iostream>
  4. #include <sstream>
  5.  
  6. std::stringstream ss;
  7.  
  8. class person
  9. {
  10. public:
  11. person()
  12. {
  13. }
  14.  
  15. person(int age)
  16. : age_(age)
  17. {
  18. }
  19.  
  20. int age() const
  21. {
  22. return age_;
  23. }
  24.  
  25. private:
  26. friend class boost::serialization::access;
  27.  
  28. template <typename Archive>
  29. friend void serialize(Archive &ar, person &p, const unsigned int version);
  30.  
  31. int age_;
  32. };
  33.  
  34. template <typename Archive>
  35. void serialize(Archive &ar, person &p, const unsigned int version)
  36. {
  37. ar & p.age_;
  38. }
  39.  
  40. void save()
  41. {
  42. boost::archive::text_oarchive oa(ss);
  43. person p(31);
  44. oa << p;
  45. }
  46.  
  47. void load()
  48. {
  49. boost::archive::text_iarchive ia(ss);
  50. person p;
  51. ia >> p;
  52. std::cout << p.age() << std::endl;
  53. }
  54.  
  55. int main()
  56. {
  57. save();
  58. load();
  59. }
  60.  

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

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

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

  1. #include <boost/archive/text_oarchive.hpp>
  2. #include <boost/archive/text_iarchive.hpp>
  3. #include <boost/serialization/string.hpp>
  4. #include <iostream>
  5. #include <sstream>
  6. #include <string>
  7.  
  8. std::stringstream ss;
  9.  
  10. class person
  11. {
  12. public:
  13. person()
  14. {
  15. }
  16.  
  17. person(int age, const std::string &name)
  18. : age_(age), name_(name)
  19. {
  20. }
  21.  
  22. int age() const
  23. {
  24. return age_;
  25. }
  26.  
  27. std::string name() const
  28. {
  29. return name_;
  30. }
  31.  
  32. private:
  33. friend class boost::serialization::access;
  34.  
  35. template <typename Archive>
  36. friend void serialize(Archive &ar, person &p, const unsigned int version);
  37.  
  38. int age_;
  39. std::string name_;
  40. };
  41.  
  42. template <typename Archive>
  43. void serialize(Archive &ar, person &p, const unsigned int version)
  44. {
  45. ar & p.age_;
  46. ar & p.name_;
  47. }
  48.  
  49. void save()
  50. {
  51. boost::archive::text_oarchive oa(ss);
  52. person p(31, "Boris");
  53. oa << p;
  54. }
  55.  
  56. void load()
  57. {
  58. boost::archive::text_iarchive ia(ss);
  59. person p;
  60. ia >> p;
  61. std::cout << p.age() << std::endl;
  62. std::cout << p.name() << std::endl;
  63. }
  64.  
  65. int main()
  66. {
  67. save();
  68. load();
  69. }
  70.  

这个例子扩展了 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 应该能够处理不带名称的旧的归档。

  1. #include <boost/archive/text_oarchive.hpp>
  2. #include <boost/archive/text_iarchive.hpp>
  3. #include <boost/serialization/string.hpp>
  4. #include <iostream>
  5. #include <sstream>
  6. #include <string>
  7.  
  8. std::stringstream ss;
  9.  
  10. class person
  11. {
  12. public:
  13. person()
  14. {
  15. }
  16.  
  17. person(int age, const std::string &name)
  18. : age_(age), name_(name)
  19. {
  20. }
  21.  
  22. int age() const
  23. {
  24. return age_;
  25. }
  26.  
  27. std::string name() const
  28. {
  29. return name_;
  30. }
  31.  
  32. private:
  33. friend class boost::serialization::access;
  34.  
  35. template <typename Archive>
  36. friend void serialize(Archive &ar, person &p, const unsigned int version);
  37.  
  38. int age_;
  39. std::string name_;
  40. };
  41.  
  42. template <typename Archive>
  43. void serialize(Archive &ar, person &p, const unsigned int version)
  44. {
  45. ar & p.age_;
  46. if (version > 0)
  47. ar & p.name_;
  48. }
  49.  
  50. BOOST_CLASS_VERSION(person, 1)
  51.  
  52. void save()
  53. {
  54. boost::archive::text_oarchive oa(ss);
  55. person p(31, "Boris");
  56. oa << p;
  57. }
  58.  
  59. void load()
  60. {
  61. boost::archive::text_iarchive ia(ss);
  62. person p;
  63. ia >> p;
  64. std::cout << p.age() << std::endl;
  65. std::cout << p.name() << std::endl;
  66. }
  67.  
  68. int main()
  69. {
  70. save();
  71. load();
  72. }
  73.  

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

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