当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>Visual C++教程

Visual C++中对象的序列化与文件I/O研究


    持久性和序列化

      持久性是对象所有的保存和加载其状态数据的能力。具有这种能力的对象能够在应用程序结束之前以某种方式将当前的对象状态数据记录下来,当程序再次运行时,通过对这些数据的读取而恢复到上一次任务结束时的状态。由于绝大多数的MFC类是直接或间接由MFC的CObject类派生出来的,因此这些MFC类都具有保存和加载对象状态的能力,是具有持久性的。在使用应用程序向导生成文档/视结构的程序框架时,就已经为应用程序提供了用于对象状态数据保存和加载的基本代码。

      为实现对象的持久性,通常多以字节流的形式将记录对象状态的数据存放到磁盘上,这种将状态数据保存到磁盘和从磁盘恢复到内存的过程称为序列化。序列化是MFC的一个重要概念,是MFC文档/视图结构应用程序能进行文档打开、保存等操作的基础。当在MFC框架程序中装载或保存一个文件时,除了打开文件以供程序读写外,还传递给应用程序一个相关的CArchive对象,并以此实现对持久性数据的序列化。

      大多数MFC应用程序在实现对象的持久性时并非直接用MFC的CFile类对磁盘文件进行读写(有关CFile类的详细介绍将在下一节进行),而是通过使用CArchive对象并由其对CFile成员函数进行调用来执行文件I/O操作。CArchive类支持复合对象的连续二进制形式的输入输出。在构造一个CArchive对象或将其连接到一个表示打开的文件的CFile对象后,可以指定一个档案是被装载还是被保存。MFC允许使用操作符“<<”和“>>”来对多种原始数据类型进行序列化。这些原始数据类型包括BYTE,WORD,LONG,DWORD,float,double,int,unsigned int,short和char等。

      对于其他由MFC类来表示的非原始数据类型如CString对象等的序列化则可以通过对“<<”和“>>”运算符的重载来解决,可以用此方式进行序列化的MFC类和结构有CString,CTime,CTimeSpan,COleVariant,COleCurreny,COleDateTime,COleDateTimeSpan,CSize,CPoint,CRect,SIZE,POINT和RECT等。除了操作符“<<”和“>>”之外还可以调用CArchive类成员函数Read()和Write()来完成序列化。下面这段代码展示了通过操作符对int型变量VarA、VarB的序列化过程:

    // 将VarA、VarB存储到档案中
    CArchive ar (&file, CArchive::store);
    ar << VarA << VarB;
    ……
    // 从档案装载VarA、VarB
    CArchive ar (&file, CArchive::load)
    ar >> VarA >> VarB;

      CArchive类仅包含有一个数据成员m__pDocument。在执行菜单上的打开或保存命令时,程序框架将会把该数据成员设置为要被序列化的文档。另外需要特别注意的是:在使用CArchive类时,要保证对CArchive对象的操作与文件访问权限的统一。

      在本文下面将要给出的示例程序中,将对绘制连线所需要的关键点坐标和坐标个数等持久性对象进行序列化。其中文档类的成员变量m_nCount和m_ptPosition[100]分别记录了当前点的个数和坐标,初始值为0。当鼠标点击客户区时将对点的个数进行累加,并保存当前点的坐标位置。随后通过Invalidate()函数发出WM_PAINT消息通知窗口对客户区进行重绘,在重绘代码中对这些点击过的点进行绘图连线:

    void CSample04View::OnLButtonDown(UINT nFlags, CPoint point)
    {
     // 获取指向文档类的指针
     CSample04Doc* pDoc = GetDocument();
     // 保存当前鼠标点击位置
     pDoc->m_ptPosition[pDoc->m_nCount] = point;
     if (pDoc->m_nCount < 100)
      pDoc->m_nCount++;
     // 刷新屏幕
     Invalidate();
     CView::OnLButtonDown(nFlags, point);
    }
    ……
    void CSample04View::OnDraw(CDC* pDC)
    {
     CSample04Doc* pDoc = GetDocument();
     ASSERT_VALID(pDoc);
     // 对点击的点进行连线绘图
     pDC->MoveTo(pDoc->m_ptPosition[0]);
     for (int i = 1; i < pDoc->m_nCount; i++)
      pDC->LineTo(pDoc->m_ptPosition[i]);
    }


      从上述程序代码不难看出,为了能保存绘制结果需要对文档类的成员变量m_nCount和m_ptPosition[100]进行序列化处理。而文档类成员函数Serialize()则通过Archive类为这些持久性对象的序列化提供了功能上的支持。下面的代码完成了对持久性对象的保存和加载:

    if (ar.IsStoring())
    {
     // 存储持久性对象到档案
     ar << m_nCount;
     for (int i = 0; i < m_nCount; i++)
      ar << m_ptPosition[i];
    }
    else
    {
     // 从档案装载持久性对象
     ar >> m_nCount;
     for (int i = 0; i < m_nCount; i++)
      ar >> m_ptPosition[i];
    }
    自定义持久类

      为了使一个类的对象成为持久的,可以自定义一个持久类,将持久性数据的存储和加载的工作交由自定义类自己去完成。这种处理方式也更加符合面向对象的程序设计要求。可以通过下面几个基本步骤来创建一个能序列化其成员变量的自定义持久类:

      1. 直接或间接从CObject类派生出一个新类。

      2. 在类的声明部分包含MFC的DECLARE_SERIAL宏,该宏只需要将类名作为参数。

      3. 重载基类的Serialize()函数,并添加对数据成员进行序列化的代码。

      4. 如果构造函数没有一个空的缺省的构造函数(不含任何参数),为其添加一个。

      5. 在类的实现部分,添加MFC的IMPLEMENT_SERIAL宏。该宏需要三个参数:类名,基类名和一个方案号。其中方案号是一个相当于版本号的整数,每当改变了类的序列化数据格式后就应当及时更改此数值。
    根据上述步骤不难对上一小节中的序列化代码进行封装,封装后的持久类CPosition负责对类成员变量m_nCount和m_ptPosition[100]的序列化,封装后的代码如下:

    // CPosition类声明部分:
    class CPosition : public CObject
    {
     DECLARE_SERIAL(CPosition)
     CPosition();
     int m_nCount;
     CPoint m_ptPosition[100];
     void Serialize(CArchive& ar);
     CPoint GetValue(int index);
     void SetValue(int index, CPoint point);
     virtual ~CPosition();
    };
    ……
    // CPosition类实现部分:
    IMPLEMENT_SERIAL(CPosition, CObject, 0)
    CPosition::CPosition()
    {
     // 对类成员进行初始化
     m_nCount = 0;
     for (int i = 0; i < 100; i++)
      m_ptPosition[i] = CPoint (0, 0);
    }
    CPosition::~CPosition()
    {
    }
    void CPosition::SetValue(int index, CPoint point)
    {
     // 设置指定点的坐标值
     m_ptPosition[index] = point;
    }
    CPoint CPosition::GetValue(int index)
    {
     // 获取指定点的坐标值
     return m_ptPosition[index];
    }
    void CPosition::Serialize(CArchive &ar)
    {
     CObject::Serialize(ar);
     if (ar.IsStoring())
     {
      // 存储持久性对象到档案
      ar << m_nCount;
      for (int i = 0; i < m_nCount; i++)
      ar << m_ptPosition[i];
     }
     else
     {
      // 从档案装载持久性对象
      ar >> m_nCount;
      for (int i = 0; i < m_nCount; i++)
       ar >> m_ptPosition[i];
     }
    }

 

共2页 首页 上一页 1 2 下一页 尾页 跳转到
相关内容
赞助商链接