为C++程序添加文件保存加载功能
生活随笔
收集整理的這篇文章主要介紹了
为C++程序添加文件保存加载功能
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、引子
為什么要浪費(fèi)時(shí)間去設(shè)計(jì)一個(gè)算法來實(shí)現(xiàn)數(shù)據(jù)的文件存儲還要費(fèi)勁地調(diào)試代碼呢?Boost庫可以為你做這些事情。借助于串行化模板,你可以容易地把數(shù)據(jù)存儲到你自己定制格式的文件中。本文將教給你如何輕松地存儲數(shù)據(jù)并回讀數(shù)據(jù)。
二、概述
當(dāng)你開發(fā)一個(gè)軟件包時(shí),你總是想集中精力于軟件的功能。而最讓你擔(dān)心的是,花費(fèi)大量的時(shí)間寫代碼,而該代碼有可能會(huì)應(yīng)用在另外大量的其他程序中。那正是重用的含義所在,你會(huì)希望另外某人已經(jīng)為你編寫出這樣現(xiàn)成的代碼。
這類問題的一個(gè)很好的例子是給予你的程序存檔的能力。例如,你可能在寫最偉大的天文學(xué)程序-在該程序中,你的用戶可以輕易地輸入時(shí)間和坐標(biāo),你的程序負(fù)責(zé)繪制當(dāng)前天空的地圖。但是,假定你賦予你的用戶能夠高亮某些星星,這樣以來它們可以容易地突出在地圖上。最后,你讓用戶能夠保存他們的配置以備后用。
你的程序集中于天文學(xué)編程。你并不是在寫一個(gè)通用庫來保存文檔,所以你不必把大量的時(shí)間花在存儲功能上,因?yàn)槟阋獙W⒂诔绦虻奶煳膶W(xué)特性。如果你是用C++編程,你可以從Boost重用庫得到幫助。為了保存文件,Boost庫包括一個(gè)串行化類,正是你需要的。
如果你成功地創(chuàng)建了你的程序工程,很可能有一個(gè)類來包含用戶信息或文檔。例如,你可能有一個(gè)類,該類列舉用戶們最喜歡的星星的名字和位置。(請?jiān)忂@里的簡化)。這就是你希望用戶能夠保存到磁盤上的數(shù)據(jù)。畢竟,幾乎所有的程序都有文件保存功能。微軟的Word保存文本和格式化數(shù)據(jù),而Excel保存工作單數(shù)據(jù)。一個(gè)優(yōu)秀的地圖程序可以用戶保存喜歡的位置,GPS路線,旅程,等等。
借助于Boost串行化庫的幫助,實(shí)現(xiàn)保存很容易-所要做是僅僅是設(shè)置好你的類,而由庫來負(fù)責(zé)其它一切-使你專注于真正的工作。
其思想是很簡單的:你創(chuàng)建了一個(gè)包含用戶數(shù)據(jù)的對象。當(dāng)準(zhǔn)備保存信息時(shí),用戶選擇File|Save As,然后從文件對話框中選擇一個(gè)文件名即可。借助于Boost,你的程序就把數(shù)據(jù)保存到選定的文件中。以后,當(dāng)用戶重新啟動(dòng)該程序時(shí),選擇 File|Open,選定已保存的文件,你的程序再一次使用Boost-但是這一次重新裝入數(shù)據(jù),因此,重新產(chǎn)生了該對象。瞧,用戶數(shù)據(jù)被回復(fù)了!或者,從用戶的角度來看,文檔已被打開。
下面的例子只是簡單地演示保存和加載一些圖形類。第一個(gè)類,Vertex,描述了一個(gè)二維的點(diǎn)。第二個(gè)類,Polygon,包含一個(gè)Vertex實(shí)例的容器。第三個(gè)類,Drawing,包含一個(gè)Polygon的容器。
想把所有這些都保存到一個(gè)文檔中去無疑是一個(gè)惡夢-這不是花費(fèi)時(shí)間的地方-你要實(shí)現(xiàn)最好的圖形程序設(shè)計(jì),因?yàn)檫@是你的專長。好了,讓Boost庫為你做其它一切吧。
三、串行化一個(gè)類
首先,考慮一下Vertex類。該類是最容易串行化的一個(gè),因?yàn)樗话渌鼘ο蟆T擃惏瑑蓚€(gè)值,x和y,且都是double型。我還給該類定義了幾個(gè)存取x和y的函數(shù),還有一個(gè)dump函數(shù),它負(fù)責(zé)把x和y的值輸送到控制臺。最后,我包含了兩個(gè)構(gòu)造器,一個(gè)是缺省的,另一個(gè)用作輸入?yún)?shù)。(為了簡化起見,該例程并沒有做任何實(shí)際的繪圖。抱歉!)
下面最吸引人的部分是必需的代碼行以串行化該類。下面就是該類(注意粗體部分):
注意在程序的最后,我沒有實(shí)際地使用缺省的構(gòu)造器Vertex(),但是串行化庫的確調(diào)用了它,因此我需要把它包含進(jìn)去。
串行化部分首先串行化庫存取私有成員,特別是接下來的串行化函數(shù)。串行化庫的創(chuàng)建者Robert Ramey指出,你不需要任何的函數(shù),包括在派生類中的那些,調(diào)用你的串行化方法;只需由串行化庫來調(diào)用即可。因此,為了保護(hù)你的類,需要把串行化功能聲 明為私有的,然后允許有限制地存取該串行化庫,這通過把類boost::serialization::access聲明為你的類的友元來實(shí)現(xiàn),見代碼。
接下去是串行化函數(shù),它是一個(gè)模板函數(shù)。如果你對模板還不太熟悉的話,不要緊:你不需要理解模板部分而照舊可以使之工作。然而,必須確保你理解了串行化功能的核心:
首先,讓我聲明一 下:這兩行代碼并不是聲明參照引用變量,雖然形式上看上去相似。代之的是,它們調(diào)用一個(gè)&操作符,并且把你的類成員寫入到文件中或者把它們讀進(jìn) 來。是的,你已經(jīng)正確地認(rèn)出了;該功能實(shí)現(xiàn)了一石二鳥(或者更準(zhǔn)確地說,用一套代碼完成了兩件任務(wù))的功效。當(dāng)你在把一個(gè)Vertex對象保存到一個(gè)文件 中去時(shí),串行化庫調(diào)用這個(gè)串行化功能;第一行把x的值寫入到文件中,第二行把y的值寫入到文件中。后來,當(dāng)你把一個(gè)Vertex對象從文件中讀回時(shí),第一 行實(shí)現(xiàn)從文件中讀回x值,第二行實(shí)現(xiàn)從文件中讀回y值。
這是某種特別的操作符重載!事實(shí)上,&字符是一個(gè)在串行化庫內(nèi)部定義的一個(gè)操作符。幸好你不需要理解它是如何工作的。
好,就是那么簡單。下面是一些示例代碼,你可以試著把一個(gè)Vertex 對象保存到一個(gè)文件中:
就是這樣!第一行產(chǎn)生Vertex對象。下面的四行打開一個(gè)文件,把一個(gè)特定的串行化類與文件相結(jié)合,然后寫向文件,最后關(guān)閉文件。下面是一段把一個(gè)Vertex 對象從文件中讀入的代碼:
這段代碼生成一個(gè)Vertex的實(shí)例,然后再打開一個(gè)文件(這次是為讀取的目的),把一個(gè)串行化類與文件相關(guān)聯(lián),把對象讀進(jìn)來,然后關(guān)閉文件。最后,代 碼輸出Vertex的值。如果你把前面的這兩個(gè)程序段放在一個(gè)main函數(shù)中并運(yùn)行,你會(huì)看到輸出兩個(gè)原始值:1.5和2.5。
注意
注意我使用的文件擴(kuò)展名是:.vtx。這并不是一個(gè)專門的擴(kuò)展名;它是我自己定制的擴(kuò)展名。這聽起來有點(diǎn)愚蠢和瑣碎,但是實(shí)際上,我們是在創(chuàng)建自己的文件格式。為了指出這一特殊的文件格式,我使用了擴(kuò)展名叫.vtx,其意指Vertex。
四、串行化容器
在我的示例中,一個(gè)繪圖對象可以包含多個(gè)多邊形對象(我把它們存儲在一個(gè)向量中,該向量是標(biāo)準(zhǔn)庫容器模板的一員),每一個(gè)多邊形對象可以包含多個(gè)對象Vertex(我也用向量存儲它們)。
串行化庫包括保存數(shù)組和容器的功能。因?yàn)槟憧梢园阎羔槾鎯Φ綌?shù)組中,串行化庫也支持指針。請考慮一下:如果你有一個(gè)包含Vertex指針的數(shù)組,而且你 直接把該數(shù)組寫入一個(gè)文件中,你就會(huì)有一堆指針存儲在文件中,而不是實(shí)際的Vertex 數(shù)據(jù)。那些指針僅是些數(shù)字(內(nèi)存位置),當(dāng)后面接著回讀數(shù)據(jù)時(shí)它們是毫無意義的。所以,該庫十分聰明地從對象中抓取了數(shù)據(jù)而不是指針。
考慮到存儲作為容器的對象,你要把每一個(gè)類串行化。在串行化方法中,你可以讀取和寫入容器,就象你操作另外一個(gè)成員一樣。你的容器可以是簡單的語言本身 內(nèi)存的數(shù)組(如Vertex *vertices[10];),或者是來自于標(biāo)準(zhǔn)庫的容器。因?yàn)楝F(xiàn)在是21世紀(jì),我喜歡緊跟時(shí)代的步伐,所以我在本例中選擇使用標(biāo)準(zhǔn)庫。
盡管你可以在你的串行化類中編寫代碼,針對容器和每一個(gè)成員;然而,你不必這樣做。作為代勞,庫已十分聰明地自動(dòng)遍歷容器了。你所有要做是僅是寫出容器,如下,其中vertices是一個(gè)容器:
讓庫來做其余的工作吧。相信嗎?下面是類Polygon的代碼,串行化部分以粗體標(biāo)出:
首先,請注意我用一個(gè)矢量來存儲布點(diǎn)。(如果你對模板還是個(gè)新手,不要緊,只需要把vector<Vertex *>當(dāng)作是存儲指向Vertex 實(shí)例的指針的矢量就行,因?yàn)槠鋵?shí)際上就是如此。)。下一步,在串行化函數(shù)中,我不想遍歷該矢量-寫每一個(gè)成員。相反,我只是讀寫整個(gè)矢量即可:
兩個(gè)公共方法的建立,可以用來十分方便 地操作該多邊形。第一個(gè)addVertex方法,讓你把另外一個(gè)結(jié)點(diǎn)添加到該多邊形上;它使用了push_back方法,這是把一項(xiàng)加到一個(gè)矢量上去的標(biāo) 準(zhǔn)方法。Dump函數(shù)遍歷該矢量,把每一個(gè)矢量寫到標(biāo)準(zhǔn)輸出設(shè)備上去。即使對一些很有經(jīng)驗(yàn)的C++老手,也可能對下面這一行不太熟悉:
這里用了點(diǎn)小技巧。它不是串行化庫的一部分;它是標(biāo)準(zhǔn)庫的一部分,對于今天的C++程序可以放心使用,沒有任何多余的副作用和經(jīng)濟(jì)問題。單詞 for_each實(shí)際上是一個(gè)函數(shù),它有三個(gè)參數(shù):在容器中的起始位置,結(jié)束條件以及一個(gè)操作容器中每一項(xiàng)都要調(diào)用的函數(shù)(依賴于你的C++程序?qū)崿F(xiàn),你 可以要包括頭文件如,’#include <algorithm>’來得到for_each函數(shù)。我使用的是GNU庫,所以用’#include <vector>’語句)。在我的例子中,我用的for_each函數(shù)的第三個(gè)參數(shù)是Vertex 類的dump成員函數(shù)。但是有一個(gè)問題:你不能只調(diào)用一個(gè)成員函數(shù)本身;你要從一個(gè)具體的對象中調(diào)用才行。這正是成員函數(shù)mem_fun的來源所在。它是 一個(gè)專門函數(shù)(標(biāo)準(zhǔn)庫的一部分),在此與函數(shù)for_each一起工作,負(fù)責(zé)調(diào)用具體對象的dump函數(shù)。也就是說,它把dump()綁定到 for_each當(dāng)前操縱的Vertex對象上。
為簡化起見,這里的for_each調(diào)用遍歷整個(gè)列表中的每一個(gè)Vertex,并調(diào)用Vertex::dump-所有這些只有一行代碼!
接下來,Drawing類實(shí)際上與Polygon類很相似,除了它包含一些Polygon 對象,而不是包含一個(gè)Vertex對象的容器。不是一個(gè)大問題。
下面是完整的程序,包含一些額外的析構(gòu)器以用于清理內(nèi)存:
記住:我盡力脫離開把繪圖對象寫入到文件中去的思想。代之的是,我只是在概念上把繪制對象當(dāng)作我的文檔,然后存儲文檔文件和讀回它們。那些文檔文件都具有我為我的程序創(chuàng)立的專門格式,而且我給予它們唯一的文件擴(kuò)展名.grp,其含義指圖形。
另外,我創(chuàng)建了幾個(gè)幫助函數(shù):getFileSaveAs和getFileOpen。在本例中這些函數(shù)僅返回一個(gè)硬編碼的字符串形式的文件名。在實(shí)際開 發(fā)中,這些函數(shù)一般會(huì)分別在菜單項(xiàng)File|Save As和File|Open中被調(diào)用;并將會(huì)調(diào)用系統(tǒng)的File|Open和File|Save對話框。這些對話框?qū)⒎祷匾粋€(gè)用戶想使用的字符串形式的文件 名。這樣,用戶的看法就同我們一樣了:打開和保存文檔,而不是讀取和寫繪圖對象數(shù)據(jù)到文檔中。要看清它們在概念上的區(qū)別,雖然它們在功能上是相同的。
五、小結(jié)
借助于Boost庫,給你的軟件增加文件的保存/打開功能相當(dāng)容易。如果你想自己試驗(yàn)這些代碼,你可以在官方站點(diǎn)找到該Boost庫,下載最新版本試驗(yàn)。
六、備注
要使用串行化庫,你最少需要得到該庫的1.32.0版本(早期的版本不包括串行化庫)。注意,在此我不是向你介紹如何安裝Boost庫;網(wǎng)站上提供詳細(xì) 步驟說明如何安裝該庫。如果你使用該串行化庫,你還需要編譯另外幾個(gè)該庫需要的.cpp源文件-你可以在boost_1_32_0\libs\ serialization\src文件夾下找到它們。還有一個(gè)boost_1_32_0\libs\serialization\build 庫,它使用了一種新的創(chuàng)建(build)系統(tǒng),稱為jamfile,你可以用它來把源文件創(chuàng)建成一個(gè)庫。或者,你可以僅把這些源文件添加到你的工程的 src目錄下。
為什么要浪費(fèi)時(shí)間去設(shè)計(jì)一個(gè)算法來實(shí)現(xiàn)數(shù)據(jù)的文件存儲還要費(fèi)勁地調(diào)試代碼呢?Boost庫可以為你做這些事情。借助于串行化模板,你可以容易地把數(shù)據(jù)存儲到你自己定制格式的文件中。本文將教給你如何輕松地存儲數(shù)據(jù)并回讀數(shù)據(jù)。
二、概述
當(dāng)你開發(fā)一個(gè)軟件包時(shí),你總是想集中精力于軟件的功能。而最讓你擔(dān)心的是,花費(fèi)大量的時(shí)間寫代碼,而該代碼有可能會(huì)應(yīng)用在另外大量的其他程序中。那正是重用的含義所在,你會(huì)希望另外某人已經(jīng)為你編寫出這樣現(xiàn)成的代碼。
這類問題的一個(gè)很好的例子是給予你的程序存檔的能力。例如,你可能在寫最偉大的天文學(xué)程序-在該程序中,你的用戶可以輕易地輸入時(shí)間和坐標(biāo),你的程序負(fù)責(zé)繪制當(dāng)前天空的地圖。但是,假定你賦予你的用戶能夠高亮某些星星,這樣以來它們可以容易地突出在地圖上。最后,你讓用戶能夠保存他們的配置以備后用。
你的程序集中于天文學(xué)編程。你并不是在寫一個(gè)通用庫來保存文檔,所以你不必把大量的時(shí)間花在存儲功能上,因?yàn)槟阋獙W⒂诔绦虻奶煳膶W(xué)特性。如果你是用C++編程,你可以從Boost重用庫得到幫助。為了保存文件,Boost庫包括一個(gè)串行化類,正是你需要的。
如果你成功地創(chuàng)建了你的程序工程,很可能有一個(gè)類來包含用戶信息或文檔。例如,你可能有一個(gè)類,該類列舉用戶們最喜歡的星星的名字和位置。(請?jiān)忂@里的簡化)。這就是你希望用戶能夠保存到磁盤上的數(shù)據(jù)。畢竟,幾乎所有的程序都有文件保存功能。微軟的Word保存文本和格式化數(shù)據(jù),而Excel保存工作單數(shù)據(jù)。一個(gè)優(yōu)秀的地圖程序可以用戶保存喜歡的位置,GPS路線,旅程,等等。
借助于Boost串行化庫的幫助,實(shí)現(xiàn)保存很容易-所要做是僅僅是設(shè)置好你的類,而由庫來負(fù)責(zé)其它一切-使你專注于真正的工作。
其思想是很簡單的:你創(chuàng)建了一個(gè)包含用戶數(shù)據(jù)的對象。當(dāng)準(zhǔn)備保存信息時(shí),用戶選擇File|Save As,然后從文件對話框中選擇一個(gè)文件名即可。借助于Boost,你的程序就把數(shù)據(jù)保存到選定的文件中。以后,當(dāng)用戶重新啟動(dòng)該程序時(shí),選擇 File|Open,選定已保存的文件,你的程序再一次使用Boost-但是這一次重新裝入數(shù)據(jù),因此,重新產(chǎn)生了該對象。瞧,用戶數(shù)據(jù)被回復(fù)了!或者,從用戶的角度來看,文檔已被打開。
下面的例子只是簡單地演示保存和加載一些圖形類。第一個(gè)類,Vertex,描述了一個(gè)二維的點(diǎn)。第二個(gè)類,Polygon,包含一個(gè)Vertex實(shí)例的容器。第三個(gè)類,Drawing,包含一個(gè)Polygon的容器。
想把所有這些都保存到一個(gè)文檔中去無疑是一個(gè)惡夢-這不是花費(fèi)時(shí)間的地方-你要實(shí)現(xiàn)最好的圖形程序設(shè)計(jì),因?yàn)檫@是你的專長。好了,讓Boost庫為你做其它一切吧。
三、串行化一個(gè)類
首先,考慮一下Vertex類。該類是最容易串行化的一個(gè),因?yàn)樗话渌鼘ο蟆T擃惏瑑蓚€(gè)值,x和y,且都是double型。我還給該類定義了幾個(gè)存取x和y的函數(shù),還有一個(gè)dump函數(shù),它負(fù)責(zé)把x和y的值輸送到控制臺。最后,我包含了兩個(gè)構(gòu)造器,一個(gè)是缺省的,另一個(gè)用作輸入?yún)?shù)。(為了簡化起見,該例程并沒有做任何實(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; } }; |
注意在程序的最后,我沒有實(shí)際地使用缺省的構(gòu)造器Vertex(),但是串行化庫的確調(diào)用了它,因此我需要把它包含進(jìn)去。
串行化部分首先串行化庫存取私有成員,特別是接下來的串行化函數(shù)。串行化庫的創(chuàng)建者Robert Ramey指出,你不需要任何的函數(shù),包括在派生類中的那些,調(diào)用你的串行化方法;只需由串行化庫來調(diào)用即可。因此,為了保護(hù)你的類,需要把串行化功能聲 明為私有的,然后允許有限制地存取該串行化庫,這通過把類boost::serialization::access聲明為你的類的友元來實(shí)現(xiàn),見代碼。
接下去是串行化函數(shù),它是一個(gè)模板函數(shù)。如果你對模板還不太熟悉的話,不要緊:你不需要理解模板部分而照舊可以使之工作。然而,必須確保你理解了串行化功能的核心:
| ar & x; ar & y; |
首先,讓我聲明一 下:這兩行代碼并不是聲明參照引用變量,雖然形式上看上去相似。代之的是,它們調(diào)用一個(gè)&操作符,并且把你的類成員寫入到文件中或者把它們讀進(jìn) 來。是的,你已經(jīng)正確地認(rèn)出了;該功能實(shí)現(xiàn)了一石二鳥(或者更準(zhǔn)確地說,用一套代碼完成了兩件任務(wù))的功效。當(dāng)你在把一個(gè)Vertex對象保存到一個(gè)文件 中去時(shí),串行化庫調(diào)用這個(gè)串行化功能;第一行把x的值寫入到文件中,第二行把y的值寫入到文件中。后來,當(dāng)你把一個(gè)Vertex對象從文件中讀回時(shí),第一 行實(shí)現(xiàn)從文件中讀回x值,第二行實(shí)現(xiàn)從文件中讀回y值。
這是某種特別的操作符重載!事實(shí)上,&字符是一個(gè)在串行化庫內(nèi)部定義的一個(gè)操作符。幸好你不需要理解它是如何工作的。
好,就是那么簡單。下面是一些示例代碼,你可以試著把一個(gè)Vertex 對象保存到一個(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對象。下面的四行打開一個(gè)文件,把一個(gè)特定的串行化類與文件相結(jié)合,然后寫向文件,最后關(guān)閉文件。下面是一段把一個(gè)Vertex 對象從文件中讀入的代碼:
| 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í)例,然后再打開一個(gè)文件(這次是為讀取的目的),把一個(gè)串行化類與文件相關(guān)聯(lián),把對象讀進(jìn)來,然后關(guān)閉文件。最后,代 碼輸出Vertex的值。如果你把前面的這兩個(gè)程序段放在一個(gè)main函數(shù)中并運(yùn)行,你會(huì)看到輸出兩個(gè)原始值:1.5和2.5。
注意
注意我使用的文件擴(kuò)展名是:.vtx。這并不是一個(gè)專門的擴(kuò)展名;它是我自己定制的擴(kuò)展名。這聽起來有點(diǎn)愚蠢和瑣碎,但是實(shí)際上,我們是在創(chuàng)建自己的文件格式。為了指出這一特殊的文件格式,我使用了擴(kuò)展名叫.vtx,其意指Vertex。
四、串行化容器
在我的示例中,一個(gè)繪圖對象可以包含多個(gè)多邊形對象(我把它們存儲在一個(gè)向量中,該向量是標(biāo)準(zhǔn)庫容器模板的一員),每一個(gè)多邊形對象可以包含多個(gè)對象Vertex(我也用向量存儲它們)。
串行化庫包括保存數(shù)組和容器的功能。因?yàn)槟憧梢园阎羔槾鎯Φ綌?shù)組中,串行化庫也支持指針。請考慮一下:如果你有一個(gè)包含Vertex指針的數(shù)組,而且你 直接把該數(shù)組寫入一個(gè)文件中,你就會(huì)有一堆指針存儲在文件中,而不是實(shí)際的Vertex 數(shù)據(jù)。那些指針僅是些數(shù)字(內(nèi)存位置),當(dāng)后面接著回讀數(shù)據(jù)時(shí)它們是毫無意義的。所以,該庫十分聰明地從對象中抓取了數(shù)據(jù)而不是指針。
考慮到存儲作為容器的對象,你要把每一個(gè)類串行化。在串行化方法中,你可以讀取和寫入容器,就象你操作另外一個(gè)成員一樣。你的容器可以是簡單的語言本身 內(nèi)存的數(shù)組(如Vertex *vertices[10];),或者是來自于標(biāo)準(zhǔn)庫的容器。因?yàn)楝F(xiàn)在是21世紀(jì),我喜歡緊跟時(shí)代的步伐,所以我在本例中選擇使用標(biāo)準(zhǔn)庫。
盡管你可以在你的串行化類中編寫代碼,針對容器和每一個(gè)成員;然而,你不必這樣做。作為代勞,庫已十分聰明地自動(dòng)遍歷容器了。你所有要做是僅是寫出容器,如下,其中vertices是一個(gè)容器:
| ar & vertices; |
讓庫來做其余的工作吧。相信嗎?下面是類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)); } }; |
首先,請注意我用一個(gè)矢量來存儲布點(diǎn)。(如果你對模板還是個(gè)新手,不要緊,只需要把vector<Vertex *>當(dāng)作是存儲指向Vertex 實(shí)例的指針的矢量就行,因?yàn)槠鋵?shí)際上就是如此。)。下一步,在串行化函數(shù)中,我不想遍歷該矢量-寫每一個(gè)成員。相反,我只是讀寫整個(gè)矢量即可:
| ar & vertices; |
兩個(gè)公共方法的建立,可以用來十分方便 地操作該多邊形。第一個(gè)addVertex方法,讓你把另外一個(gè)結(jié)點(diǎn)添加到該多邊形上;它使用了push_back方法,這是把一項(xiàng)加到一個(gè)矢量上去的標(biāo) 準(zhǔn)方法。Dump函數(shù)遍歷該矢量,把每一個(gè)矢量寫到標(biāo)準(zhǔn)輸出設(shè)備上去。即使對一些很有經(jīng)驗(yàn)的C++老手,也可能對下面這一行不太熟悉:
| for_each(vertices.begin(), vertices.end(), mem_fun(&Vertex::dump)); |
這里用了點(diǎn)小技巧。它不是串行化庫的一部分;它是標(biāo)準(zhǔn)庫的一部分,對于今天的C++程序可以放心使用,沒有任何多余的副作用和經(jīng)濟(jì)問題。單詞 for_each實(shí)際上是一個(gè)函數(shù),它有三個(gè)參數(shù):在容器中的起始位置,結(jié)束條件以及一個(gè)操作容器中每一項(xiàng)都要調(diào)用的函數(shù)(依賴于你的C++程序?qū)崿F(xiàn),你 可以要包括頭文件如,’#include <algorithm>’來得到for_each函數(shù)。我使用的是GNU庫,所以用’#include <vector>’語句)。在我的例子中,我用的for_each函數(shù)的第三個(gè)參數(shù)是Vertex 類的dump成員函數(shù)。但是有一個(gè)問題:你不能只調(diào)用一個(gè)成員函數(shù)本身;你要從一個(gè)具體的對象中調(diào)用才行。這正是成員函數(shù)mem_fun的來源所在。它是 一個(gè)專門函數(shù)(標(biāo)準(zhǔn)庫的一部分),在此與函數(shù)for_each一起工作,負(fù)責(zé)調(diào)用具體對象的dump函數(shù)。也就是說,它把dump()綁定到 for_each當(dāng)前操縱的Vertex對象上。
為簡化起見,這里的for_each調(diào)用遍歷整個(gè)列表中的每一個(gè)Vertex,并調(diào)用Vertex::dump-所有這些只有一行代碼!
接下來,Drawing類實(shí)際上與Polygon類很相似,除了它包含一些Polygon 對象,而不是包含一個(gè)Vertex對象的容器。不是一個(gè)大問題。
下面是完整的程序,包含一些額外的析構(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: //串行化代碼開始 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í)際開發(fā)中,這將調(diào)用一個(gè)各種樣的FileOpen 對話框 return "c:/myfile.grp"; } string getFileSaveAs() { //在實(shí)際開發(fā)中,這將調(diào)用一個(gè)各種樣的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); //演示保存一個(gè)文檔 saveDocument(draw, getFileSaveAs()); // 演示打開一個(gè)文檔 string filename2 = getFileOpen(); Drawing *doc2 = openDocument(getFileOpen()); doc2->dump(); delete draw; return 0; } |
記住:我盡力脫離開把繪圖對象寫入到文件中去的思想。代之的是,我只是在概念上把繪制對象當(dāng)作我的文檔,然后存儲文檔文件和讀回它們。那些文檔文件都具有我為我的程序創(chuàng)立的專門格式,而且我給予它們唯一的文件擴(kuò)展名.grp,其含義指圖形。
另外,我創(chuàng)建了幾個(gè)幫助函數(shù):getFileSaveAs和getFileOpen。在本例中這些函數(shù)僅返回一個(gè)硬編碼的字符串形式的文件名。在實(shí)際開 發(fā)中,這些函數(shù)一般會(huì)分別在菜單項(xiàng)File|Save As和File|Open中被調(diào)用;并將會(huì)調(diào)用系統(tǒng)的File|Open和File|Save對話框。這些對話框?qū)⒎祷匾粋€(gè)用戶想使用的字符串形式的文件 名。這樣,用戶的看法就同我們一樣了:打開和保存文檔,而不是讀取和寫繪圖對象數(shù)據(jù)到文檔中。要看清它們在概念上的區(qū)別,雖然它們在功能上是相同的。
五、小結(jié)
借助于Boost庫,給你的軟件增加文件的保存/打開功能相當(dāng)容易。如果你想自己試驗(yàn)這些代碼,你可以在官方站點(diǎn)找到該Boost庫,下載最新版本試驗(yàn)。
六、備注
要使用串行化庫,你最少需要得到該庫的1.32.0版本(早期的版本不包括串行化庫)。注意,在此我不是向你介紹如何安裝Boost庫;網(wǎng)站上提供詳細(xì) 步驟說明如何安裝該庫。如果你使用該串行化庫,你還需要編譯另外幾個(gè)該庫需要的.cpp源文件-你可以在boost_1_32_0\libs\ serialization\src文件夾下找到它們。還有一個(gè)boost_1_32_0\libs\serialization\build 庫,它使用了一種新的創(chuàng)建(build)系統(tǒng),稱為jamfile,你可以用它來把源文件創(chuàng)建成一個(gè)庫。或者,你可以僅把這些源文件添加到你的工程的 src目錄下。
總結(jié)
以上是生活随笔為你收集整理的为C++程序添加文件保存加载功能的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《我也能做CTO之.程序员职业规划》 水
- 下一篇: 缩小数据库文件