日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

为C++程序添加文件保存加载功能

發(fā)布時(shí)間:2023/12/19 c/c++ 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 为C++程序添加文件保存加载功能 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、引子

  為什么要浪費(fèi)時(shí)間去設(shè)計(jì)一個(gè)算法來(lái)實(shí)現(xiàn)數(shù)據(jù)的文件存儲(chǔ)還要費(fèi)勁地調(diào)試代碼呢?Boost庫(kù)可以為你做這些事情。借助于串行化模板,你可以容易地把數(shù)據(jù)存儲(chǔ)到你自己定制格式的文件中。本文將教給你如何輕松地存儲(chǔ)數(shù)據(jù)并回讀數(shù)據(jù)。

  二、概述

  當(dāng)你開(kāi)發(fā)一個(gè)軟件包時(shí),你總是想集中精力于軟件的功能。而最讓你擔(dān)心的是,花費(fèi)大量的時(shí)間寫(xiě)代碼,而該代碼有可能會(huì)應(yīng)用在另外大量的其他程序中。那正是重用的含義所在,你會(huì)希望另外某人已經(jīng)為你編寫(xiě)出這樣現(xiàn)成的代碼。

  這類問(wèn)題的一個(gè)很好的例子是給予你的程序存檔的能力。例如,你可能在寫(xiě)最偉大的天文學(xué)程序-在該程序中,你的用戶可以輕易地輸入時(shí)間和坐標(biāo),你的程序負(fù)責(zé)繪制當(dāng)前天空的地圖。但是,假定你賦予你的用戶能夠高亮某些星星,這樣以來(lái)它們可以容易地突出在地圖上。最后,你讓用戶能夠保存他們的配置以備后用。

  你的程序集中于天文學(xué)編程。你并不是在寫(xiě)一個(gè)通用庫(kù)來(lái)保存文檔,所以你不必把大量的時(shí)間花在存儲(chǔ)功能上,因?yàn)槟阋獙W⒂诔绦虻奶煳膶W(xué)特性。如果你是用C++編程,你可以從Boost重用庫(kù)得到幫助。為了保存文件,Boost庫(kù)包括一個(gè)串行化類,正是你需要的。

  如果你成功地創(chuàng)建了你的程序工程,很可能有一個(gè)類來(lái)包含用戶信息或文檔。例如,你可能有一個(gè)類,該類列舉用戶們最喜歡的星星的名字和位置。(請(qǐng)?jiān)忂@里的簡(jiǎn)化)。這就是你希望用戶能夠保存到磁盤(pán)上的數(shù)據(jù)。畢竟,幾乎所有的程序都有文件保存功能。微軟的Word保存文本和格式化數(shù)據(jù),而Excel保存工作單數(shù)據(jù)。一個(gè)優(yōu)秀的地圖程序可以用戶保存喜歡的位置,GPS路線,旅程,等等。

  借助于Boost串行化庫(kù)的幫助,實(shí)現(xiàn)保存很容易-所要做是僅僅是設(shè)置好你的類,而由庫(kù)來(lái)負(fù)責(zé)其它一切-使你專注于真正的工作。

  其思想是很簡(jiǎn)單的:你創(chuàng)建了一個(gè)包含用戶數(shù)據(jù)的對(duì)象。當(dāng)準(zhǔn)備保存信息時(shí),用戶選擇File|Save As,然后從文件對(duì)話框中選擇一個(gè)文件名即可。借助于Boost,你的程序就把數(shù)據(jù)保存到選定的文件中。以后,當(dāng)用戶重新啟動(dòng)該程序時(shí),選擇 File|Open,選定已保存的文件,你的程序再一次使用Boost-但是這一次重新裝入數(shù)據(jù),因此,重新產(chǎn)生了該對(duì)象。瞧,用戶數(shù)據(jù)被回復(fù)了!或者,從用戶的角度來(lái)看,文檔已被打開(kāi)。

  下面的例子只是簡(jiǎn)單地演示保存和加載一些圖形類。第一個(gè)類,Vertex,描述了一個(gè)二維的點(diǎn)。第二個(gè)類,Polygon,包含一個(gè)Vertex實(shí)例的容器。第三個(gè)類,Drawing,包含一個(gè)Polygon的容器。

  想把所有這些都保存到一個(gè)文檔中去無(wú)疑是一個(gè)惡夢(mèng)-這不是花費(fèi)時(shí)間的地方-你要實(shí)現(xiàn)最好的圖形程序設(shè)計(jì),因?yàn)檫@是你的專長(zhǎng)。好了,讓Boost庫(kù)為你做其它一切吧。

  三、串行化一個(gè)類

  首先,考慮一下Vertex類。該類是最容易串行化的一個(gè),因?yàn)樗话渌鼘?duì)象。該類包含兩個(gè)值,x和y,且都是double型。我還給該類定義了幾個(gè)存取x和y的函數(shù),還有一個(gè)dump函數(shù),它負(fù)責(zé)把x和y的值輸送到控制臺(tái)。最后,我包含了兩個(gè)構(gòu)造器,一個(gè)是缺省的,另一個(gè)用作輸入?yún)?shù)。(為了簡(jiǎn)化起見(jiàn),該例程并沒(méi)有做任何實(shí)際的繪圖。抱歉!)

  下面最吸引人的部分是必需的代碼行以串行化該類。下面就是該類(注意粗體部分):

class Vertex {
 private:
  friend class boost::serialization::access;
  template<class Archive>
  void serialize(Archive & ar, const unsigned int version)
  {
   ar & x;
   ar & y;
  }
  double x;
  double y;
 public:
  Vertex() {} // 串行化需要一個(gè)缺省的構(gòu)造器
  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;
  }
};

  注意在程序的最后,我沒(méi)有實(shí)際地使用缺省的構(gòu)造器Vertex(),但是串行化庫(kù)的確調(diào)用了它,因此我需要把它包含進(jìn)去。

   串行化部分首先串行化庫(kù)存取私有成員,特別是接下來(lái)的串行化函數(shù)。串行化庫(kù)的創(chuàng)建者Robert Ramey指出,你不需要任何的函數(shù),包括在派生類中的那些,調(diào)用你的串行化方法;只需由串行化庫(kù)來(lái)調(diào)用即可。因此,為了保護(hù)你的類,需要把串行化功能聲 明為私有的,然后允許有限制地存取該串行化庫(kù),這通過(guò)把類boost::serialization::access聲明為你的類的友元來(lái)實(shí)現(xiàn),見(jiàn)代碼。

  接下去是串行化函數(shù),它是一個(gè)模板函數(shù)。如果你對(duì)模板還不太熟悉的話,不要緊:你不需要理解模板部分而照舊可以使之工作。然而,必須確保你理解了串行化功能的核心:

ar & x;
ar & y;

  首先,讓我聲明一 下:這兩行代碼并不是聲明參照引用變量,雖然形式上看上去相似。代之的是,它們調(diào)用一個(gè)&操作符,并且把你的類成員寫(xiě)入到文件中或者把它們讀進(jìn) 來(lái)。是的,你已經(jīng)正確地認(rèn)出了;該功能實(shí)現(xiàn)了一石二鳥(niǎo)(或者更準(zhǔn)確地說(shuō),用一套代碼完成了兩件任務(wù))的功效。當(dāng)你在把一個(gè)Vertex對(duì)象保存到一個(gè)文件 中去時(shí),串行化庫(kù)調(diào)用這個(gè)串行化功能;第一行把x的值寫(xiě)入到文件中,第二行把y的值寫(xiě)入到文件中。后來(lái),當(dāng)你把一個(gè)Vertex對(duì)象從文件中讀回時(shí),第一 行實(shí)現(xiàn)從文件中讀回x值,第二行實(shí)現(xiàn)從文件中讀回y值。

  這是某種特別的操作符重載!事實(shí)上,&字符是一個(gè)在串行化庫(kù)內(nèi)部定義的一個(gè)操作符。幸好你不需要理解它是如何工作的。

  好,就是那么簡(jiǎn)單。下面是一些示例代碼,你可以試著把一個(gè)Vertex 對(duì)象保存到一個(gè)文件中:

Vertex v1(1.5, 2.5);
std::ofstream ofs("myfile.vtx");
boost::archive::text_oarchive oa(ofs);
oa << v1;
ofs.close();

  就是這樣!第一行產(chǎn)生Vertex對(duì)象。下面的四行打開(kāi)一個(gè)文件,把一個(gè)特定的串行化類與文件相結(jié)合,然后寫(xiě)向文件,最后關(guān)閉文件。下面是一段把一個(gè)Vertex 對(duì)象從文件中讀入的代碼:

Vertex v2;
std::ifstream ifs("myfile.vtx", std::ios::binary);
boost::archive::text_iarchive ia(ifs);
ia >> v2;
ifs.close();
v2.dump();

   這段代碼生成一個(gè)Vertex的實(shí)例,然后再打開(kāi)一個(gè)文件(這次是為讀取的目的),把一個(gè)串行化類與文件相關(guān)聯(lián),把對(duì)象讀進(jìn)來(lái),然后關(guān)閉文件。最后,代 碼輸出Vertex的值。如果你把前面的這兩個(gè)程序段放在一個(gè)main函數(shù)中并運(yùn)行,你會(huì)看到輸出兩個(gè)原始值:1.5和2.5。

  注意

  注意我使用的文件擴(kuò)展名是:.vtx。這并不是一個(gè)專門(mén)的擴(kuò)展名;它是我自己定制的擴(kuò)展名。這聽(tīng)起來(lái)有點(diǎn)愚蠢和瑣碎,但是實(shí)際上,我們是在創(chuàng)建自己的文件格式。為了指出這一特殊的文件格式,我使用了擴(kuò)展名叫.vtx,其意指Vertex。
  四、串行化容器

  在我的示例中,一個(gè)繪圖對(duì)象可以包含多個(gè)多邊形對(duì)象(我把它們存儲(chǔ)在一個(gè)向量中,該向量是標(biāo)準(zhǔn)庫(kù)容器模板的一員),每一個(gè)多邊形對(duì)象可以包含多個(gè)對(duì)象Vertex(我也用向量存儲(chǔ)它們)。

   串行化庫(kù)包括保存數(shù)組和容器的功能。因?yàn)槟憧梢园阎羔槾鎯?chǔ)到數(shù)組中,串行化庫(kù)也支持指針。請(qǐng)考慮一下:如果你有一個(gè)包含Vertex指針的數(shù)組,而且你 直接把該數(shù)組寫(xiě)入一個(gè)文件中,你就會(huì)有一堆指針存儲(chǔ)在文件中,而不是實(shí)際的Vertex 數(shù)據(jù)。那些指針僅是些數(shù)字(內(nèi)存位置),當(dāng)后面接著回讀數(shù)據(jù)時(shí)它們是毫無(wú)意義的。所以,該庫(kù)十分聰明地從對(duì)象中抓取了數(shù)據(jù)而不是指針。

   考慮到存儲(chǔ)作為容器的對(duì)象,你要把每一個(gè)類串行化。在串行化方法中,你可以讀取和寫(xiě)入容器,就象你操作另外一個(gè)成員一樣。你的容器可以是簡(jiǎn)單的語(yǔ)言本身 內(nèi)存的數(shù)組(如Vertex *vertices[10];),或者是來(lái)自于標(biāo)準(zhǔn)庫(kù)的容器。因?yàn)楝F(xiàn)在是21世紀(jì),我喜歡緊跟時(shí)代的步伐,所以我在本例中選擇使用標(biāo)準(zhǔn)庫(kù)。

  盡管你可以在你的串行化類中編寫(xiě)代碼,針對(duì)容器和每一個(gè)成員;然而,你不必這樣做。作為代勞,庫(kù)已十分聰明地自動(dòng)遍歷容器了。你所有要做是僅是寫(xiě)出容器,如下,其中vertices是一個(gè)容器:

ar & vertices;

  讓庫(kù)來(lái)做其余的工作吧。相信嗎?下面是類Polygon的代碼,串行化部分以粗體標(biāo)出:

class Polygon {
 private:
  vector<Vertex *> vertices;
  friend class boost::serialization::access;
  template<class 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));
  }
};

   首先,請(qǐng)注意我用一個(gè)矢量來(lái)存儲(chǔ)布點(diǎn)。(如果你對(duì)模板還是個(gè)新手,不要緊,只需要把vector<Vertex *>當(dāng)作是存儲(chǔ)指向Vertex 實(shí)例的指針的矢量就行,因?yàn)槠鋵?shí)際上就是如此。)。下一步,在串行化函數(shù)中,我不想遍歷該矢量-寫(xiě)每一個(gè)成員。相反,我只是讀寫(xiě)整個(gè)矢量即可:

ar & vertices;

  兩個(gè)公共方法的建立,可以用來(lái)十分方便 地操作該多邊形。第一個(gè)addVertex方法,讓你把另外一個(gè)結(jié)點(diǎn)添加到該多邊形上;它使用了push_back方法,這是把一項(xiàng)加到一個(gè)矢量上去的標(biāo) 準(zhǔn)方法。Dump函數(shù)遍歷該矢量,把每一個(gè)矢量寫(xiě)到標(biāo)準(zhǔn)輸出設(shè)備上去。即使對(duì)一些很有經(jīng)驗(yàn)的C++老手,也可能對(duì)下面這一行不太熟悉:

for_each(vertices.begin(), vertices.end(), mem_fun(&Vertex::dump));

   這里用了點(diǎn)小技巧。它不是串行化庫(kù)的一部分;它是標(biāo)準(zhǔn)庫(kù)的一部分,對(duì)于今天的C++程序可以放心使用,沒(méi)有任何多余的副作用和經(jīng)濟(jì)問(wèn)題。單詞 for_each實(shí)際上是一個(gè)函數(shù),它有三個(gè)參數(shù):在容器中的起始位置,結(jié)束條件以及一個(gè)操作容器中每一項(xiàng)都要調(diào)用的函數(shù)(依賴于你的C++程序?qū)崿F(xiàn),你 可以要包括頭文件如,’#include <algorithm>’來(lái)得到for_each函數(shù)。我使用的是GNU庫(kù),所以用’#include <vector>’語(yǔ)句)。在我的例子中,我用的for_each函數(shù)的第三個(gè)參數(shù)是Vertex 類的dump成員函數(shù)。但是有一個(gè)問(wèn)題:你不能只調(diào)用一個(gè)成員函數(shù)本身;你要從一個(gè)具體的對(duì)象中調(diào)用才行。這正是成員函數(shù)mem_fun的來(lái)源所在。它是 一個(gè)專門(mén)函數(shù)(標(biāo)準(zhǔn)庫(kù)的一部分),在此與函數(shù)for_each一起工作,負(fù)責(zé)調(diào)用具體對(duì)象的dump函數(shù)。也就是說(shuō),它把dump()綁定到 for_each當(dāng)前操縱的Vertex對(duì)象上。

  為簡(jiǎn)化起見(jiàn),這里的for_each調(diào)用遍歷整個(gè)列表中的每一個(gè)Vertex,并調(diào)用Vertex::dump-所有這些只有一行代碼!

  接下來(lái),Drawing類實(shí)際上與Polygon類很相似,除了它包含一些Polygon 對(duì)象,而不是包含一個(gè)Vertex對(duì)象的容器。不是一個(gè)大問(wèn)題。

  下面是完整的程序,包含一些額外的析構(gòu)器以用于清理內(nèi)存:

#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.hpp>
using namespace std;
class Vertex {
 private:
  //串行化代碼開(kāi)始
  friend class boost::serialization::access;
  template<class Archive>
  void serialize(Archive & ar, unsigned int version)
  {
   ar & x;
   ar & y;
  }
  //結(jié)束串行化代碼
  double x;
  double y;
 public:
  Vertex() {} //串行化需要一個(gè)缺省的構(gòu)造器
  ~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:
  vector<Vertex *> vertices;
  friend class boost::serialization::access;
  template<class 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:
  vector<Polygon *> polygons;
  friend class boost::serialization::access;
  template<class 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() {
 //在實(shí)際開(kāi)發(fā)中,這將調(diào)用一個(gè)各種樣的FileOpen 對(duì)話框
 return "c:/myfile.grp";
}
string getFileSaveAs() {
 //在實(shí)際開(kāi)發(fā)中,這將調(diào)用一個(gè)各種樣的FileSave 對(duì)話框
 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);
 //演示保存一個(gè)文檔
 saveDocument(draw, getFileSaveAs());
 // 演示打開(kāi)一個(gè)文檔
 string filename2 = getFileOpen();
 Drawing *doc2 = openDocument(getFileOpen());
 doc2->dump();
 delete draw;
 return 0;
}

  記住:我盡力脫離開(kāi)把繪圖對(duì)象寫(xiě)入到文件中去的思想。代之的是,我只是在概念上把繪制對(duì)象當(dāng)作我的文檔,然后存儲(chǔ)文檔文件和讀回它們。那些文檔文件都具有我為我的程序創(chuàng)立的專門(mén)格式,而且我給予它們唯一的文件擴(kuò)展名.grp,其含義指圖形。

   另外,我創(chuàng)建了幾個(gè)幫助函數(shù):getFileSaveAs和getFileOpen。在本例中這些函數(shù)僅返回一個(gè)硬編碼的字符串形式的文件名。在實(shí)際開(kāi) 發(fā)中,這些函數(shù)一般會(huì)分別在菜單項(xiàng)File|Save As和File|Open中被調(diào)用;并將會(huì)調(diào)用系統(tǒng)的File|Open和File|Save對(duì)話框。這些對(duì)話框?qū)⒎祷匾粋€(gè)用戶想使用的字符串形式的文件 名。這樣,用戶的看法就同我們一樣了:打開(kāi)和保存文檔,而不是讀取和寫(xiě)繪圖對(duì)象數(shù)據(jù)到文檔中。要看清它們?cè)诟拍钌系膮^(qū)別,雖然它們?cè)诠δ苌鲜窍嗤摹?br />
  五、小結(jié)

  借助于Boost庫(kù),給你的軟件增加文件的保存/打開(kāi)功能相當(dāng)容易。如果你想自己試驗(yàn)這些代碼,你可以在官方站點(diǎn)找到該Boost庫(kù),下載最新版本試驗(yàn)。

  六、備注

   要使用串行化庫(kù),你最少需要得到該庫(kù)的1.32.0版本(早期的版本不包括串行化庫(kù))。注意,在此我不是向你介紹如何安裝Boost庫(kù);網(wǎng)站上提供詳細(xì) 步驟說(shuō)明如何安裝該庫(kù)。如果你使用該串行化庫(kù),你還需要編譯另外幾個(gè)該庫(kù)需要的.cpp源文件-你可以在boost_1_32_0\libs\ serialization\src文件夾下找到它們。還有一個(gè)boost_1_32_0\libs\serialization\build 庫(kù),它使用了一種新的創(chuàng)建(build)系統(tǒng),稱為jamfile,你可以用它來(lái)把源文件創(chuàng)建成一個(gè)庫(kù)。或者,你可以僅把這些源文件添加到你的工程的 src目錄下。

總結(jié)

以上是生活随笔為你收集整理的为C++程序添加文件保存加载功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。