在第一部分,我們學到了如何通過CArchive類的serialize()函數來序列化一個簡單對象。象下面的程序這樣:
int CFoo::serialize
(CArchive* pArchive)
{
int nStatus = SUCCESS;
// Serialize the object ...
ASSERT (pArchive != NULL);
TRY
{
if (pArchive->IsStoring()) {
// Write employee name and id
(*pArchive) << m_strName;
(*pArchive) << m_nId;
}
else {
// Read employee name and id
(*pArchive) >> m_strName;
(*pArchive) >> m_nId;
}
}
CATCH_ALL (pException)
{
nStatus = ERROR;
}
END_CATCH_ALL
return (nStatus);
}
這段代碼有一個問題,如果我們錯誤的從數據文件中去讀取並不存在的信息會怎麼樣?如果數據文件中在CString之後不是一個整數,那麼serialize()函數將返回ERROR。這也不錯,但是如果我們可以給錯誤定位,並返回一個更加詳細的錯誤信息,比如INVALID_DATAFILE,那就更好了。我們可以通過對象簽名來確認我們是在讀取一個有效的數據文件。
對象簽名
對象簽名就是用一個字符串來識別一個對象。我們修改Cfoo類的定義來增加一個簽名:
class CFoo
{
...
// Methods
public:
...
CString getSignature();
// Data members
...
protected:
static const CString Signature; // object signature
};
簽名在Foo.cpp中申明:
// Static constants
const CString CFoo::Signature = "FooObject";
下一步,我們修改serialize()函數,在序列化對象的數據成員前先序列化這個簽名。如果遇到無效的簽名,或者簽名丟失,那麼可以認為我們在試圖讀取一個並不包含有Cfoo對象的數據存儲。以下是讀取有簽名的對象的流程:
以下是流程實現代碼:
int CFoo::serialize
(CArchive* pArchive)
{
int nStatus = SUCCESS;
bool bSignatureRead = false;
// Serialize the object ...
ASSERT (pArchive != NULL);
TRY
{
if (pArchive->IsStoring()) {
// Write signature
(*pArchive) << getSignature();
// Write employee name and id
(*pArchive) << m_strName;
(*pArchive) << m_nId;
}
else {
// Read signature - complain if invalid
CString strSignature;
(*pArchive) >> strSignature;
bSignatureRead = true;
if (strSignature.Compare (getSignature()) != 0) {
return (INVALID_DATAFILE);
}
// Read employee name and id
(*pArchive) >> m_strName;
(*pArchive) >> m_nId;
}
}
CATCH_ALL (pException)
{
nStatus = bSignatureRead ? ERROR : INVALID_DATAFILE;
}
END_CATCH_ALL
return (nStatus);
}
你必須確保你所有的對象都有唯一的簽名。具體的簽名內容並不重要。如果你正在開發一批產品,那麼在公司范圍內增加一個對象簽名注冊步驟是非常有幫助的。另一方面,開發者將不會給不同的對象使用相同的簽名。如果你想讓你的數據文件更難以被反解,那麼你應該使用一個與對象名稱沒有明顯任何關系的簽名。
版本
如果你在產品的生命周期內進行升級,那麼你也許要通過增加或者刪除數據成員來修改Cfoo類結構。如果你只是為Cfoo發布新的版本,並且試圖從數據存儲中讀取舊版本的對象,那麼將會失敗。這明顯是不可以接受的。Cfoo的任何版本都應該可以讓一個更舊版本恢復。
換句話說,Cfoo的序列化方法應該是向前兼容的。這個問題通過給對象增加版本是很容易完成的。與給對象增加簽名相同,我們增加一個整數常量來指定這個對象的版本號。
class CFoo
{
...
// Methods
public:
...
CString getSignature();
int getVersion();
// Data members
...
protected:
static const CString Signature; // object signature
static const int Version; // object version
};
對象的版本號在Foo.cpp中申明
// Static constants
const CString CFoo::Signature = "FooObject";
const int CFoo::Version = 1;
下一步,我們修改serialize()函數,在序列化簽名之後,序列化對象的數據成員之前序列化版本。如果遇到更新的版本,我們試圖讀取一個對象不支持的版本,那麼,在下面的例子中,我們就返回一個狀態標志UNSUPPORTED_VERSION。
int CFoo::serialize
(CArchive* pArchive)
{
int nStatus = SUCCESS;
bool bSignatureRead = false;
bool bVersionRead = false;
// Serialize the object ...
ASSERT (pArchive != NULL);
TRY
{
if (pArchive->IsStoring()) {
// Write signature and version
(*pArchive) << getSignature();
(*pArchive) << getVersion();
// Write employee name and id
(*pArchive) << m_strName;
(*pArchive) << m_nId;
}
<