新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → 使用 MFC 串行化数据和 C++ 对象 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 4384 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 使用 MFC 串行化数据和 C++ 对象 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 使用 MFC 串行化数据和 C++ 对象

    串行化数据

    ——例子程序:Memo

      创建一个新的单文档 SDI 应用,视图类选择 CFormView,以便用户可以在窗口中输入。 在界面中创建三个编辑框,然后再添加三个相应的编辑框变量。这三个变量是视图类的成员变量,为了交互数据,文档类中也要创建三个对应的变量。然后,文档类和视图类都要对数据成员进行初始化操作,在文档类中这个工作通常都在 OnNewDocument() 函数中进行。因为下面任何一个操作发生时都触发文档类 OnNewDocument()函数执行:

    当用户启动应用程序;
    当用户在“File”菜单中选择“New”选项;
    视图类的初始化通常由 OnInitialUpdate() 负责,下面的任何一个操作发生时,代码都会触发视图类 OnInitialUpdate()函数执行 :

    当用户启动应用程序;
    当用户在“File”菜单中选择“New”选项;
    当用户从“File”菜单中选择 “Open”选项;
    在视图类中获得文档类指针的方法是:CFooDoc* pDoc = GerDocument();
    用此文档指针便可以操作文档类数据:m_ViewData = pDoc->m_DocData;

    串行化的代码很简单,ar 是一个与用户选择的文件相对应的文档对象(CArchive 对象):

    // CFooDoc 序列化
    void CFooDoc::Serialize(CArchive& ar)
    {
     if (ar.IsStoring())
     {
      // 将数据写入文件
       ar << m_DocData;
     }
     else
     {
      // 从文件中读取数据
       ar >> m_DocData;
     }
    }
      这样就将数据写入了文件,选择“File”菜单中的“Save”或者“Save as”即可完成数据的串行化。 如果没有保存数据,退出程序是会提示用户是否保存修改过的数据。具体细节请参考源代码。

    串行化C++对象

    ——例子程序:PHN

    创建一个新的单文档 SDI 应用,视图类选择 CFormView,以便可以有窗口中用户可以输入。

    声明一个要串行化的 C++ 类。如 CPhone;

    文档类的处理:
      在文档类中声明一个 MFC CObList 类对象,这个类很有用,功能也很强,用它可以很轻松地维护 C++ 对象列表,例如 添加、删除列表元素等。在文档类的头文件中作如下声明:

    CObList m_PhoneList;
      上面的声明可以是 public 类型,这样其它类可以直接访问它。也可以是 private 类型,这样就必须声明一个公共的访问函数,比如:GetPhoneList(),这个函数能返回 m_PhoneList 的地址。

    通常可以在文档类的 OnNewDocument()函数中进行数据初始化;

     // Create a CPhone Object
     CPhone* pPhone = new CPhone();
     pPhone->m_Name = "";
     pPhone->m_Phone = "";

     // Add new object to the m_PhoneList list
     m_PhoneList.AddHead(pPhone);  
      在此 CPhone 类的成员变量的初始化不是必须的,因为 CPhone 的构造函数已经完成了这个工作。AddHead()函数向 m_PhoneList 列表添加刚创建的 CPhone 对象。所以,无论什么时候创建新文档(如启动应用程序)都会向 m_PhoneList 列表中添加一个空的 CPhone 对象。注意类 CObList 的成员函数 AddHead() 是向列表的“头部”添加对象(列表的开始),所以参数是想要添加的对象的地址。
    删除 m_PhoneList 列表中的内容

      因为 m_PhoneList 是在内存中维护的,所以要随时维护,只要下面三个事件中的任何一个事件发生,都需要从内存中删除 m_PhoneList 列表中的对象:

    用户退出应用程序;
    用户开始一个新的文档,如从“File”菜单中选择“New”选项;
    用户打开一个已存在的文档,如从“File”菜单中选择“Open”选项;
    在文档类的头文件中声明删除操作的函数:

    virtual void DeleteContents();
    其实现如下:

    // 删除列表中的所有项目并释放列表对象占用的内存
    while ( ! m_PhoneList.IsEmpty() )
    {
     delete m_PhoneList.RemoveHead();
    }

    视图类处理:

    声明视图类的数据成员:

    POSITION m_position; // 在文档类列表中的当前位置
    CObList* m_pList; // 指向文档类的列表
    在 OnInitialUpdate()函数中初始化视图类的数据成员

     POSITION m_position;  
     CObList* m_pList;     


     // 获取文档类指针
     CFooDoc* pDoc = (CFooDoc*) GetDocument();

     // 获得文档类 m_PhoneList 的地址
     m_pList = &(pDoc->m_PhoneList);

     // 获得列表头位置
     m_position = m_pList->GetHeadPosition();

     // 用文档类数据更新视图类数据成员
     CPhone* pPhone = (CPhone*)m_pList->GetAt(m_position);
     m_Name = pPhone->m_Name;
     m_Phone = pPhone->m_Phone;

     // 用新的数据成员变量值更新屏幕显示
     UpdateData(FALSE);

     // 控制输入焦点
     ((CDialog*) this)->GotoDlgCtrl(GetDlgItem(IDC_NAME));

    更新文档数据

    当用户修改了视图类的数据成员,即修改了窗体编辑框中的内容时,执行这些代码后也会修改文档类的数据成员。

    void CFooView::OnEnChangeName()
    {
     // 用屏幕输入更新控件变量
     UpdateData(TRUE);

     // 获得文档指针
     CFooDoc* pDoc =(CFooDoc*)GetDocument();

     // 更新文档
     CPhone* pPhone = (CPhone*)m_pList->GetAt(m_position);
     pPhone->m_Name = m_Name;

     // 置修改标志为 TRUE
     pDoc->SetModifiedFlag();
    }

    在列表中移动记录,修改视图类中相应的函数。

     // 声明一个临时的位置变量
     POSITION temp_pos;

     // 用当前的列表位置更新 temp_pos
     temp_pos = m_position;

     // 用前一个/或后一个位置更新 temp_pos
     m_pList->GetPrev(temp_pos);

     if ( temp_pos == NULL)
     {
      // no previous element
      MessageBox(_T("Bottom of file encountered!"),_T("Phone for Windows"));

     }else
     {
      // 用列表前一个记录内容更新视图成员数据
      m_position = temp_pos;
      CPhone* pPhone = (CPhone*)m_pList->GetAt(m_position);
      m_Name = pPhone->m_Name;
      m_Phone = pPhone->m_Phone;
      UpdateData(FALSE);
     }
     // 控制输入焦点
     ((CDialog*) this)->GotoDlgCtrl(GetDlgItem(IDC_NAME));

    添加和删除列表记录:

    //添加记录
     // 清空屏幕输入控制
     m_Name = "";
     m_Phone = "";
     UpdateData(FALSE);

     // 创建一个新的  CPhone 对象
     CPhone* pPhone = new CPhone();
     pPhone->m_Name = m_Name;
     pPhone->m_Phone = m_Phone;

     // 添加新的对象到列表尾部,并用新的位置更新 m_position
     m_position = m_pList->AddTail(pPhone);

     // 获得文档指针
     CFooDoc* pDoc = (CFooDoc*) GetDocument();

     // 置修改标志为 TRUE
     pDoc->SetModifiedFlag();

     // 控制输入焦点
     ((CDialog*) this)->GotoDlgCtrl(this->GetDlgItem(IDC_NAME));

    //删除记录
     // 删除前先保存旧的指针
     CObject* pOld;
     pOld = m_pList->GetAt(m_position);

     // 从列表中删除元素
     m_pList->RemoveAt(m_position);

     // 从内存中删除对象
     delete pOld;

     // 如果列表已经清空则添加一个空记录
     if ( m_pList->IsEmpty())
     {
      OnBnClickedAddButton();
     }

     // 获取文档指针
     CPHNDoc* pDoc = (CPHNDoc*) GetDocument();

     // 置修改标志为 TRUE
     pDoc->SetModifiedFlag();

     // 显示列表的第一条记录
     OnInitialUpdate();


    串行化处理

      我们要串行化 CPhone 对象,把C++对象写入文件,所以需要在 CPhone 类的定义和实现文件中加入相应的串行化代码,首先要在 CPhone 头文件中加入一个 MFC 宏,这是串行化需要的宏,必须为它提供一个参数,也就是类的名字。

    // 串行化宏定义
    DECLARE_SERIAL(CPhone)
      其次是声明串行化函数,这个原型是必须的,因为要串行化类 CPhone 对象列表,所以 CPhone 类必须有一个属于自己的 Serialize()函数:// 串行化函数 Serialize()
    virtual void Serialize(CArchive& ar);

      在 CPhone 实现文件中也要加入对应的代码,这个宏也是串行化需要的另一个宏,它有三个参数,第一个是类名,第二个是基类名,第三个是应用程序的版本号,可以将版本号定义为任何值,当串行化数据到文件时,此版本号也要写入文件。

    // 串行化宏实现
    IMPLEMENT_SERIAL(CPhone,CObject,0);
    串行化函数 Serialize() 实现 if (ar.IsStoring())
    {
      ar << m_Name << m_Phone;
    }
    else
    {
      ar >> m_Name >> m_Phone;
    }

    这里要注意的是为了使用 CObList 类的成员函数 Serialize(),有几个前提条件需要满足:

    列表类对象必须是 MFC CObject 类的派生类对象,也就是说 CPhone 类必须是 CObject 的派生类;
    在列表中的对象类必须具备一个不带参数的构造函数。如果需要,也可以有其它带参数的构造函数;
    必须声明和实现列表类的串行化函数 Serialize(),即 CPhone::Serialize();
    实现列表对象的串行化必须使用 DECLARE_SERIAL/IMPLEMENT_SERIAL 宏;
    调用列表 Serialize()函数

      这一步是串行化列表 m_PhoneList,也就是调用 m_PhoneList 的成员函数 Serialize()。在什么地方调用呢?记住,无论用户什么时候从“File”菜单中选择“Save”或者“Save as”或“Open”选项,都将执行文档类的 Serialize()函数,所以必须在文档类的 Serialize()函数中调用 m_PhoneList 的 Serialize()函数。
      这样一来,无论用户什么时候从 File 菜单中选择 Save/Save as 时,都将把 m_PhoneList 保存在用户选择的文件中,同样地,无论用户什么时候从选择 Open 时,都将把文件中保存的列表信息加载到 m_PhoneList 中来。m_PhoneList 的串行化调用如下:

    m_PhoneList.Serialize(ar);
      只要在文档类的 Serialize() 函数中调用上面这条语句时,必须把 ar 作为参数传入,它将完成需要串行化 m_PhoneList 列表数据的所有工作。不必在if语句中再做其它处理。

    定制串行化


    ——例子程序:ARCH

      串行化处理有时并不需要用户选择文件,此时仍要从或向一个特定文件串行化数据,本部分将描述怎样创建并定制一个 CArchive 对象。创建一个新的单文档 SDI 应用, 工程名为 ARCH。视图类仍然选择 CFormView。视图中两个编辑框和两个按钮,编辑框用于输入数据,“Save to File”按钮用于将输入的数据串行化到文件,“Load from File”按钮用于从文件中抽取数据。为简单起见,文件使用的硬编码。
    下面是 “Save to File”的操作代码:

     // 用屏幕输入内容更新 m_Var1 和 m_Var2
     UpdateData(TRUE);

     // 创建文件 C:\ARC.ARC
     CFile f;
     f.Open("c:\\arc.arc",CFile::modeCreate|CFile::modeWrite);

     // 创建一个 CArchive 对象,并将文件与对象关联
     CArchive ar(&f,CArchive::store);

     // 串行化 m_Var1 和 m_Var2 到文档
     ar<<m_Var1<<m_Var2;

     // 关闭文档
     ar.Close();

     // 关闭文件
     f.Close();

    下面是 “Load from File”的操作代码:

     // 打开文件 C:\ARC.ARC
     CFile f;
     if ( f.Open("c:\\arc.arc",CFile::modeRead ) == FALSE )
      return;

     // 创建一个 CArchive 对象,并将文件与对象关联
     CArchive ar(&f,CArchive::load);

     // 从对象中抽取数据并赋值给成员变量
     ar>>m_Var1>>m_Var2;

     // 关闭文档
     ar.Close();

     // 关闭文件
     f.Close();

     // 更新屏幕显示
     UpdateData(FALSE);

      以上是三个 MFC 串行化数据的例子,Memo 程序的功能是串行化数据到文件,Phn 程序是串行化 C++ 对象列表到文件,而 ARCH 则是定制串行化。


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2010/9/28 8:56:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/11/25 22:05:54

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    62.500ms