低面效果在哪个网站做,建立公司网站多少钱,网络公共关系,国外网络推广方法一、引子为什么要浪费时间去设计一个算法来实现数据的文件存储还要费劲地调试代码呢#xff1f;Boost库可以为你做这些事情。借助于串行化模板#xff0c;你可以容易地把数据存储到你自己定制格式的文件中。本文将教给你如何轻松地存储数据并回读数据。二、概述当你开发一个软…一、引子 为什么要浪费时间去设计一个算法来实现数据的文件存储还要费劲地调试代码呢Boost库可以为你做这些事情。借助于串行化模板你可以容易地把数据存储到你自己定制格式的文件中。本文将教给你如何轻松地存储数据并回读数据。 二、概述 当你开发一个软件包时你总是想集中精力于软件的功能。而最让你担心的是花费大量的时间写代码而该代码有可能会应用在另外大量的其他程序中。那正是重用的含义所在你会希望另外某人已经为你编写出这样现成的代码。 这类问题的一个很好的例子是给予你的程序存档的能力。例如你可能在写最伟大的天文学程序-在该程序中你的用户可以轻易地输入时间和坐标你的程序负责绘制当前天空的地图。但是假定你赋予你的用户能够高亮某些星星这样以来它们可以容易地突出在地图上。最后你让用户能够保存他们的配置以备后用。 你的程序集中于天文学编程。你并不是在写一个通用库来保存文档所以你不必把大量的时间花在存储功能上因为你要专注于程序的天文学特性。如果你是用C编程你可以从Boost重用库得到帮助。为了保存文件Boost库包括一个串行化类正是你需要的。 如果你成功地创建了你的程序工程很可能有一个类来包含用户信息或文档。例如你可能有一个类该类列举用户们最喜欢的星星的名字和位置。请原谅这里的简化。这就是你希望用户能够保存到磁盘上的数据。毕竟几乎所有的程序都有文件保存功能。微软的Word保存文本和格式化数据而Excel保存工作单数据。一个优秀的地图程序可以用户保存喜欢的位置GPS路线旅程等等。 借助于Boost串行化库的帮助实现保存很容易-所要做是仅仅是设置好你的类而由库来负责其它一切-使你专注于真正的工作。 其思想是很简单的你创建了一个包含用户数据的对象。当准备保存信息时用户选择FileSave As然后从文件对话框中选择一个文件名即可。借助于Boost你的程序就把数据保存到选定的文件中。以后当用户重新启动该程序时选择 FileOpen选定已保存的文件你的程序再一次使用Boost-但是这一次重新装入数据因此重新产生了该对象。瞧用户数据被回复了或者从用户的角度来看文档已被打开。 下面的例子只是简单地演示保存和加载一些图形类。第一个类Vertex描述了一个二维的点。第二个类Polygon包含一个Vertex实例的容器。第三个类Drawing包含一个Polygon的容器。 想把所有这些都保存到一个文档中去无疑是一个恶梦-这不是花费时间的地方-你要实现最好的图形程序设计因为这是你的专长。好了让Boost库为你做其它一切吧。 三、串行化一个类 首先考虑一下Vertex类。该类是最容易串行化的一个因为它不包含其它对象。该类包含两个值x和y且都是double型。我还给该类定义了几个存取x和y的函数还有一个dump函数它负责把x和y的值输送到控制台。最后我包含了两个构造器一个是缺省的另一个用作输入参数。为了简化起见该例程并没有做任何实际的绘图。抱歉 下面最吸引人的部分是必需的代码行以串行化该类。下面就是该类注意粗体部分class Vertex { private: friend class boost::serialization::access; templateclass Archive void serialize(Archive ar, const unsigned int version) { ar x; ar y; } double x; double y; public: Vertex() {} // 串行化需要一个缺省的构造器 Vertex(double newX, double newY) : x(newX), y(newY) {} double getX() const { return x; } double getY() const { return y; } void dump() { cout x y endl; } }; 注意在程序的最后我没有实际地使用缺省的构造器Vertex()但是串行化库的确调用了它因此我需要把它包含进去。 串行化部分首先串行化库存取私有成员特别是接下来的串行化函数。串行化库的创建者Robert Ramey指出你不需要任何的函数包括在派生类中的那些调用你的串行化方法只需由串行化库来调用即可。因此为了保护你的类需要把串行化功能声 明为私有的然后允许有限制地存取该串行化库这通过把类boost::serialization::access声明为你的类的友元来实现见代码。 接下去是串行化函数它是一个模板函数。如果你对模板还不太熟悉的话不要紧你不需要理解模板部分而照旧可以使之工作。然而必须确保你理解了串行化功能的核心ar x;ar y; 首先让我声明一 下这两行代码并不是声明参照引用变量虽然形式上看上去相似。代之的是它们调用一个操作符并且把你的类成员写入到文件中或者把它们读进 来。是的你已经正确地认出了该功能实现了一石二鸟或者更准确地说用一套代码完成了两件任务的功效。当你在把一个Vertex对象保存到一个文件 中去时串行化库调用这个串行化功能第一行把x的值写入到文件中第二行把y的值写入到文件中。后来当你把一个Vertex对象从文件中读回时第一 行实现从文件中读回x值第二行实现从文件中读回y值。 这是某种特别的操作符重载事实上字符是一个在串行化库内部定义的一个操作符。幸好你不需要理解它是如何工作的。 好就是那么简单。下面是一些示例代码你可以试着把一个Vertex 对象保存到一个文件中Vertex v1(1.5, 2.5);std::ofstream ofs(myfile.vtx);boost::archive::text_oarchive oa(ofs);oa v1;ofs.close(); 就是这样第一行产生Vertex对象。下面的四行打开一个文件把一个特定的串行化类与文件相结合然后写向文件最后关闭文件。下面是一段把一个Vertex 对象从文件中读入的代码Vertex v2;std::ifstream ifs(myfile.vtx, std::ios::binary);boost::archive::text_iarchive ia(ifs);ia v2;ifs.close();v2.dump(); 这段代码生成一个Vertex的实例然后再打开一个文件这次是为读取的目的把一个串行化类与文件相关联把对象读进来然后关闭文件。最后代 码输出Vertex的值。如果你把前面的这两个程序段放在一个main函数中并运行你会看到输出两个原始值1.5和2.5。 注意 注意我使用的文件扩展名是.vtx。这并不是一个专门的扩展名它是我自己定制的扩展名。这听起来有点愚蠢和琐碎但是实际上我们是在创建自己的文件格式。为了指出这一特殊的文件格式我使用了扩展名叫.vtx其意指Vertex。 四、串行化容器 在我的示例中一个绘图对象可以包含多个多边形对象我把它们存储在一个向量中该向量是标准库容器模板的一员每一个多边形对象可以包含多个对象Vertex我也用向量存储它们。 串行化库包括保存数组和容器的功能。因为你可以把指针存储到数组中串行化库也支持指针。请考虑一下如果你有一个包含Vertex指针的数组而且你 直接把该数组写入一个文件中你就会有一堆指针存储在文件中而不是实际的Vertex 数据。那些指针仅是些数字内存位置当后面接着回读数据时它们是毫无意义的。所以该库十分聪明地从对象中抓取了数据而不是指针。 考虑到存储作为容器的对象你要把每一个类串行化。在串行化方法中你可以读取和写入容器就象你操作另外一个成员一样。你的容器可以是简单的语言本身 内存的数组如Vertex *vertices[10];或者是来自于标准库的容器。因为现在是21世纪我喜欢紧跟时代的步伐所以我在本例中选择使用标准库。 尽管你可以在你的串行化类中编写代码针对容器和每一个成员然而你不必这样做。作为代劳库已十分聪明地自动遍历容器了。你所有要做是仅是写出容器如下其中vertices是一个容器ar vertices; 让库来做其余的工作吧。相信吗下面是类Polygon的代码串行化部分以粗体标出class Polygon { private: vectorVertex * vertices; friend class boost::serialization::access; templateclass Archive void serialize(Archive ar, const unsigned int version) { ar vertices; } public: void addVertex(Vertex *v) { vertices.push_back(v); } void dump() { for_each(vertices.begin(), vertices.end(), mem_fun(Vertex::dump)); }}; 首先请注意我用一个矢量来存储布点。如果你对模板还是个新手不要紧只需要把vectorVertex *当作是存储指向Vertex 实例的指针的矢量就行因为其实际上就是如此。。下一步在串行化函数中我不想遍历该矢量-写每一个成员。相反我只是读写整个矢量即可ar vertices; 两个公共方法的建立可以用来十分方便 地操作该多边形。第一个addVertex方法让你把另外一个结点添加到该多边形上它使用了push_back方法这是把一项加到一个矢量上去的标 准方法。Dump函数遍历该矢量把每一个矢量写到标准输出设备上去。即使对一些很有经验的C老手也可能对下面这一行不太熟悉 for_each(vertices.begin(), vertices.end(), mem_fun(Vertex::dump)); 这里用了点小技巧。它不是串行化库的一部分它是标准库的一部分对于今天的C程序可以放心使用没有任何多余的副作用和经济问题。单词 for_each实际上是一个函数它有三个参数在容器中的起始位置结束条件以及一个操作容器中每一项都要调用的函数依赖于你的C程序实现你 可以要包括头文件如’#include algorithm’来得到for_each函数。我使用的是GNU库所以用’#include vector’语句。在我的例子中我用的for_each函数的第三个参数是Vertex 类的dump成员函数。但是有一个问题你不能只调用一个成员函数本身你要从一个具体的对象中调用才行。这正是成员函数mem_fun的来源所在。它是 一个专门函数标准库的一部分在此与函数for_each一起工作负责调用具体对象的dump函数。也就是说它把dump()绑定到 for_each当前操纵的Vertex对象上。 为简化起见这里的for_each调用遍历整个列表中的每一个Vertex并调用Vertex::dump-所有这些只有一行代码 接下来Drawing类实际上与Polygon类很相似除了它包含一些Polygon 对象而不是包含一个Vertex对象的容器。不是一个大问题。 下面是完整的程序包含一些额外的析构器以用于清理内存#include iostream#include vector#include functional#include algorithm#include fstream#include boost/archive/text_oarchive.hpp#include boost/archive/text_iarchive.hpp#include boost/serialization/vector.hppusing namespace std;class Vertex { private: //串行化代码开始 friend class boost::serialization::access; templateclass Archive void serialize(Archive ar, unsigned int version) { ar x; ar y; } //结束串行化代码 double x; double y; public: Vertex() {} //串行化需要一个缺省的构造器 ~Vertex() {} Vertex(double newX, double newY) : x(newX), y(newY) {} double getX() const { return x; } double getY() const { return y; } void dump() { cout x y endl; } };void delete_vertex(Vertex *v) { delete v; }class Polygon { private: vectorVertex * vertices; friend class boost::serialization::access; templateclass Archive void serialize(Archive ar, const unsigned int version) { ar vertices; } public: ~Polygon() { for_each(vertices.begin(), vertices.end(), delete_vertex); } void addVertex(Vertex *v) { vertices.push_back(v); } void dump() { for_each(vertices.begin(), vertices.end(), mem_fun(Vertex::dump)); }};void delete_poly(Polygon *p) { delete p; }class Drawing { private: vectorPolygon * polygons; friend class boost::serialization::access; templateclass Archive void serialize(Archive ar, const unsigned int version) { ar polygons; } public: ~Drawing() { for_each(polygons.begin(), polygons.end(), delete_poly); } void addPolygon(Polygon *p) { polygons.push_back(p); } void dump() { for_each(polygons.begin(), polygons.end(), mem_fun(Polygon::dump)); }};string getFileOpen() { //在实际开发中这将调用一个各种样的FileOpen 对话框 return c:/myfile.grp;}string getFileSaveAs() { //在实际开发中这将调用一个各种样的FileSave 对话框 return c:/myfile.grp;}void saveDocument(Drawing *doc, const string filename) { ofstream ofs(filename.c_str()); boost::archive::text_oarchive oa(ofs); oa *doc; ofs.close();}Drawing *openDocument(const string filename) { Drawing *doc new Drawing(); std::ifstream ifs(filename.c_str(), std::ios::binary); boost::archive::text_iarchive ia(ifs); ia *doc; ifs.close(); return doc;}int main(){ Polygon *poly1 new Polygon(); poly1-addVertex(new Vertex(0.1,0.2)); poly1-addVertex(new Vertex(1.5,1.5)); poly1-addVertex(new Vertex(0.5,2.9)); Polygon *poly2 new Polygon(); poly2-addVertex(new Vertex(0,0)); poly2-addVertex(new Vertex(0,1.5)); poly2-addVertex(new Vertex(1.5,1.5)); poly2-addVertex(new Vertex(1.5,0)); Drawing *draw new Drawing(); draw-addPolygon(poly1); draw-addPolygon(poly2); //演示保存一个文档 saveDocument(draw, getFileSaveAs()); // 演示打开一个文档 string filename2 getFileOpen(); Drawing *doc2 openDocument(getFileOpen()); doc2-dump(); delete draw; return 0; } 记住我尽力脱离开把绘图对象写入到文件中去的思想。代之的是我只是在概念上把绘制对象当作我的文档然后存储文档文件和读回它们。那些文档文件都具有我为我的程序创立的专门格式而且我给予它们唯一的文件扩展名.grp其含义指图形。 另外我创建了几个帮助函数getFileSaveAs和getFileOpen。在本例中这些函数仅返回一个硬编码的字符串形式的文件名。在实际开 发中这些函数一般会分别在菜单项FileSave As和FileOpen中被调用并将会调用系统的FileOpen和FileSave对话框。这些对话框将返回一个用户想使用的字符串形式的文件 名。这样用户的看法就同我们一样了打开和保存文档而不是读取和写绘图对象数据到文档中。要看清它们在概念上的区别虽然它们在功能上是相同的。 五、小结 借助于Boost库给你的软件增加文件的保存/打开功能相当容易。如果你想自己试验这些代码你可以在官方站点找到该Boost库下载最新版本试验。 六、备注 要使用串行化库你最少需要得到该库的1.32.0版本早期的版本不包括串行化库。注意在此我不是向你介绍如何安装Boost库;网站上提供详细 步骤说明如何安装该库。如果你使用该串行化库你还需要编译另外几个该库需要的.cpp源文件-你可以在boost_1_32_0\libs\ serialization\src文件夹下找到它们。还有一个boost_1_32_0\libs\serialization\build 库它使用了一种新的创建build系统称为jamfile你可以用它来把源文件创建成一个库。或者你可以仅把这些源文件添加到你的工程的 src目录下。