首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MFC 序列化類別版本控制:VERSIONABLE_SCHEMA 實作完整指南

     編輯:關於VC++

問題背景

MFC 序列化機制使用結構號進行版本控制。寫入檔案時標記結構號,讀取時比對,不符則拋出 CArchiveException (badSchema),導致「非預期的檔案格式」錯誤。若每次修改儲存格式都遞增結構號,可避免新版程式讀取舊版資料失敗。

但實務上常需「向下相容」:新版程式需同時支援舊版資料格式。此時可使用「可版本化結構號」。

可版本化結構號機制

將 IMPLEMENT_SERIAL 宏的結構號與 VERSIONABLE_SCHEMA 進行位元或運算:

IMPLEMENT_SERIAL(CLine, CObject, 2 | VERSIONABLE_SCHEMA)

此標記告知 MFC:該類別可處理多種序列化格式,抑制 CArchiveException,允許程式在 Serialize() 中透過 CArchive::GetObjectSchema() 判斷版本並對應處理。

GetObjectSchema 使用規則

  1. 僅能在物件序列化時呼叫
  2. 必須在讀取物件資料之前呼叫
  3. 整個序列化過程中只能呼叫一次,重複呼叫回傳 -1

完整範例:CLine 類別演進

版本 1

class CLine : public CObject
{
    DECLARE_SERIAL(CLine)
protected:
    CPoint m_ptFrom, m_ptTo;
public:
    CLine() {}
    CLine(CPoint from, CPoint to) { m_ptFrom = from; m_ptTo = to; }
    void Serialize(CArchive& ar);
};

void CLine::Serialize(CArchive& ar)
{
    CObject::Serialize(ar);
    if (ar.IsStoring())
        ar << m_ptFrom << m_ptTo;
    else
        ar >> m_ptFrom >> m_ptTo;
}

IMPLEMENT_SERIAL(CLine, CObject, 1)

版本 2:新增顏色屬性

class CLine : public CObject
{
    DECLARE_SERIAL(CLine)
protected:
    CPoint m_ptFrom, m_ptTo;
    COLORREF m_clrLine;  // 新增
public:
    CLine() {}
    CLine(CPoint from, CPoint to, COLORREF color)
        { m_ptFrom = from; m_ptTo = to; m_clrLine = color; }
    void Serialize(CArchive& ar);
};

IMPLEMENT_SERIAL(CLine, CObject, 2 | VERSIONABLE_SCHEMA)

void CLine::Serialize(CArchive& ar)
{
    CObject::Serialize(ar);
    if (ar.IsStoring()) {
        ar << m_ptFrom << m_ptTo << m_clrLine;  // 統一寫入最新版
    }
    else {
        UINT nSchema = ar.GetObjectSchema();
        switch (nSchema) {
        case 1:  // 版本 1 資料
            ar >> m_ptFrom >> m_ptTo;
            m_clrLine = RGB(0, 0, 0);  // 預設顏色
            break;
        case 2:  // 版本 2 資料
            ar >> m_ptFrom >> m_ptTo >> m_clrLine;
            break;
        default:
            AfxThrowArchiveException(CArchiveException::badSchema);
        }
    }
}

實務應用範例:CSquareDoc 俄羅斯方塊遊戲存檔

宣告與 IMPLEMENT

class CSquareDoc : public CDocument
{
    DECLARE_SERIAL(CSquareDoc)
    // ... 成員變數
};

IMPLEMENT_SERIAL(CSquareDoc, CDocument, 2 | VERSIONABLE_SCHEMA)

Serialize 實作

void CSquareDoc::Serialize(CArchive& ar)
{
    ar.SerializeClass(RUNTIME_CLASS(CSquareDoc));
    CDocument::Serialize(ar);

    if (ar.IsStoring()) {
        SaveData(ar);  // 僅按最新版本儲存
    }
    else {
        UINT nSchema = ar.GetObjectSchema();
        switch (nSchema) {
        case 1: LoadDataV1(ar); break;
        case 2: LoadDataV2(ar); break;
        default: AfxThrowArchiveException(CArchiveException::badSchema);
        }
    }
}

版本 1 載入 (無炸彈/導彈/原子彈,單色)

void CSquareDoc::LoadDataV1(CArchive& ar)
{
    ar >> iSpeed >> fenshu;
    iBomb = iMissile = iA_bomb = 0;

    for (int i = 0; i < SQUARE_AMOUNT_y; i++)
        for (int j = 0; j < SQUARE_AMOUNT_x; j++) {
            ar >> mc[j][i].CellPos.x >> mc[j][i].CellPos.y
               >> mc[j][i].IsFillFlag >> mc[j][i].size.cx >> mc[j][i].size.cy;
            mc[j][i].rgb = RGB(0, 255, 0);
        }
    // 方塊與預知方塊同理,rgb 固定綠色
}

版本 2 載入 (完整資料)

void CSquareDoc::LoadDataV2(CArchive& ar)
{
    ar >> iSpeed >> fenshu >> iBomb >> iMissile >> iA_bomb;
    // 完整載入地圖、方塊、預知方塊所有欄位含 rgb
}

總結

使用 VERSIONABLE_SCHEMA 搭配 GetObjectSchema() 切換邏輯,即可優雅實現 MFC 序列化的多版本相容。未來擴充只需遞增版本號並新增 case 分支即可。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved