查看文章 |
在MFC编写的程序中定义和生成了各种各样的对象,通过它们之间的协同操作完成程序的功能。所以在MFC程序中,程序的存取操作的核心是如何实现这些对象的持续化。一个可以实现持续化的对象知道如何保存和载入其自己的数据,例如在程序中使用MFC 文档/视图结构时,如果为文档对象提供正确的持续化功能,它将在需要时自动地保存和恢复自己的数据,并且保持用户最新的修改结果。需要注意的是对象的持续化同样是将数据保存到磁盘文件中,好处在于MFC对二进制文件的存取过程进行了封装,用来实现对象存取的程序代码得到了简化。 但是一些应用程序操作数据的方式不适合存档序列化的方式,这类应用程序包括数据库程序、只编辑部分大型文件的程序、仅处理文本文件的程序和共享数据文件的程序等。在这些程序中可以用不同的方法重写Serialize函数,通过CFile对象,而不是通过CArchive对象处理文件操作。可以使用CFile类的Open、 Read、Write、Close和Seek成员函数打开文件,将文件指针移动到(查找)文件的指定的特定位置。在该位置上读取记录(指定的字节数),并允许用户更新记录,然后再次查找同一位置并将记录写回文件。框架将打开文件,可以用CArchive类的GetFile成员函数获取指向CFile对象的指针。对于更复杂和更灵活的使用,可以重写CWinApp类的OnOpenDocument和OnSaveDocument成员函数。
本文讨论如何实现一般意义上的文件存取处理。 在MFC编写的程序中定义和生成了各种各样的对象,通过它们之间的协同操作完成程序的功能。所以在MFC程序中,程序的存取操作的核心是如何实现这些对象的持续化。一个可以实现持续化的对象知道如何保存和载入其自己的数据,例如在程序中使用MFC 文档/视图结构时,如果为文档对象提供正确的持续化功能,它将在需要时自动地保存和恢复自己的数据,并且保持用户最新的修改结果。需要注意的是对象的持续化同样是将数据保存到磁盘文件中,好处在于MFC对二进制文件的存取过程进行了封装,用来实现对象存取的程序代码得到了简化。 但是一些应用程序操作数据的方式不适合存档序列化的方式,这类应用程序包括数据库程序、只编辑部分大型文件的程序、仅处理文本文件的程序和共享数据文件的程序等。在这些程序中可以用不同的方法重写Serialize函数,通过CFile对象,而不是通过CArchive对象处理文件操作。可以使用CFile类的Open、 Read、Write、Close和Seek成员函数打开文件,将文件指针移动到(查找)文件的指定的特定位置。在该位置上读取记录(指定的字节数),并允许用户更新记录,然后再次查找同一位置并将记录写回文件。框架将打开文件,可以用CArchive类的GetFile成员函数获取指向CFile对象的指针。对于更复杂和更灵活的使用,可以重写CWinApp类的OnOpenDocument和OnSaveDocument成员函数。 一、序列化序列化是指将对象写入永久性保存媒体(如媒体文件)或从其中读取对象的进程。MFC对CObject类中的序列化提供内置支持,因此所有从CObject派生的类都可以利用CObject的序列化协议。 序列化的基本思想是对象应将其当前状态(通常由该对象的成员变量指示)写入永久性文件中,以后通过从保存中读取对象状态或反序列化对象状态可以重新创建该对象。对于可序列化的类,必须实现基本的序列化操作。 MFC将CArchive类的对象用作被序列化的对象和保存媒体间的中介,该对象始终与CFile对象相关联。它从CFile对象获得序列化所需的信息,包括文件名和请求的操作是读取还是写入。执行序列化操作的对象可以不考虑媒体的情况下使用CArchive对象。 1、创建可序列化的类使类可序列化需要5个步骤,如果直接调用Serialize,而不是通过CArchive的">>"和"<<"运算符调用,则序列化不需要最后3个步骤。 (1)从CObject派生(或从CObject派生类中派生)。CObject类中定义了基本的序列化协议和功能。 (2)重写Serialize成员函数。其参数CArchive对象具有成员函数IsStoring,指示Serialize正在保存还是读取。 例如某个类定义: class CPerson:public CObject { public: DECLARE_SERIAL(CPerson) CPerson(){}; CString m_name; WORD m_number; void Serialize(CArchive& ar); //... }; Serialize的实现: void CPerson::Serialize(CArchive& ar) { // 调用基类的Serialize CObject::Serialize(ar); if (ar.IsStoring()) ar << m_name << m_number; else ar >> m_name >> m_number; } (3)在支持序列化的类的声明中需要DECLARE_SERIAL宏。 (4)反序列化对象(从磁盘加载)后,MFC重新创建这些对象时需要一个默认的构造函数。反序列化进程将用重新创建对象所需的值填充所有成员变量,可将该构造函数声明为公共的、受保护的或私有的。 (5)类的实现文件中使用IMPLEMENT_SERIAL宏。该宏的前两个参数是类名和直接基类名,第3个参数是构架编号,即实质上的类对象版本号,它使用大于或等于0的整数,MFC序列化代码在将对象读取到内存时检查该架构编号。如果磁盘上对象的架构编号与内存中类的架构编号不匹配,库将引发 CArchiveException,防止程序读取对象的不同版本。如果要读取多个版本,可使用VERSIONABLE_SCHEMA值。 如IMPLEMENT_SERIAL(CPerson, CObject, 1) 2、序列化对象CArchive对象提供了一个类型安全缓冲机制,用于将可序列化对象写入CFile对象或者从中读取可序列化对象。通常,CFile对象表示磁盘文件,但是也可以是表示剪贴板的内存文件(CSharedFile对象)。 给定的CArchive对象要么保存,要么读取,不能同时进行。其寿命只限于将对象写入文件或从文件读取对象的一次传递。 当存档将对象附加到文件时,将CRuntimeClass名称附加到这些对象。然后当另一个存档对象从文件加载到内存时,将基于这些对象的 CRuntimeClass动态的重新构造CObject派生的对象。通过保存存档将给定对象写入文件时,该对象可能被引用多次,然而加载存档将仅对该对象重新构造一次。 将数据序列化到存档时,存档积累数据,直到其缓冲区被填满为止,然后存档将其缓冲区写入CArchive对象指向的CFile对象中。同样从存档中读取数据时,存档会将数据从文件读取到它的缓冲区,然后从缓冲区读取到反序列化的对象。这种缓冲减少了物理读取硬盘的次数,从而提高了应用程序的性能。 (1)创建CArchive对象的两种方法方法一:使用框架代表"文件"下拉菜单中的"保存"、"另存为"和"打开"选项,为文档创建CArchive对象。显示"另存"对话框->打开用户命名的文件作为CFile对象->创建指向CFile对象的 CArchive对象(store)->调用CDocument派生类中定义的Serialize函数,将CArchive对象的引用传递给该函数 ->文档的Serialize对象将数据写入CArchive对象。从Serialize返回时,框架销毁CArichive对象后销毁CFile 对象。 如果由框架为文档创建CArchive对象,所要做的工作是实现写入存档和从存档中读取的文档的Serialize函数,还必须为文档的Serialize函数直接或间接依次序列化的任何CObject派生对象的Serialize。 方法二:显式的创建CArchive对象。 构造CFile对象或从CFile到出对象; 将CFile对象传递给CArchive的构造函数; 关闭对象。 CFile theFile; TheFile.Open(..., CFile::modeWrite); CArchive archive(&theFile, CArchive::store); // 读取操作 WORD wEmployeeID; archive << wEmployeeID; // 关闭对象 archive.Close(); theFile.Close(); (3)通过存档保存和加载CObject使用CArchive的<<和>>运算符还是调用Serialize,取决于是否需要加载存档基于先前保存的CRuntimeClass信息动态地重新构造对象。在下列情况使用Serialize函数。 ·反序列化对象时,预先知道对象地确切类 ·反序列化对象时,已为其分配了内存。 如果使用Serialize加载对象,也必须使用Serialize保存对象。但可以用Serialize保存,然后使用>>加载。 总之,如果可序列化的类嵌入地CObject定义为成员,则应调用Serialize。如果可序列化地类将指向CObject(或CObject派生类)的指针定义为成员,但在自己的构造函数中将其构造为其他对象,则也应该调用Serialize函数。 二、使用CFile类在MFC中,CFile类处理正常的文件I/O操作,它提供通用二进制文件操作的接口。从CFile类派生的CStdioFile和CMemFile类,以及从CMemFile类派生的CSharedFile类提供了更专门化的文件服务。 CFile类的方法如下: (1)构造·Abort:关闭文件忽略所有警告和错误; ·CFile:从路径或文件句柄构造CFile对象; ·Close:关闭文件并删除对象; ·Duplicate:清除任何要写的数据; ·Open:用错误测试选项,安全的打开文件 (2)输入/输出·Flush:清除刚被写入的数据; ·Read:从文件当前位置读取数据; ·Write:把文件中的数据写到当前位置; (3)定位·GetLength:获取文件长度; ·Seek:放置当前文件的指针 ·SeekToBegin:把当前文件指针放在文件的开始处; ·SeekToEnd:把当前文件指针放在文件的结束处; ·SetLength:改变文件长度; (4)锁定·LockRange:锁定文件中的字节范围; ·UnlockRange:解锁文件中的一组字节; (5)状态·GetFileName:获取所选文件的文件名 ·GetFilePath:获取所选文件的全路径; ·GetFileTitle:获取所选文件的标题 ·GetPosition:获取当前文件的指针; ·GetStatus:获取当前文件的状态; ·SetFilePath:设置所选文件的全路径; (6)静态·GetStatus:获取指定文件的状态(静态或虚拟函数) ·Remove:删除指定文件 ·Rename:重新命名指定文件 ·SetStatus:设置指定文件的状态。 你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=961408 |