何为序列化
作者:佘新伟 up项目组
序列化是将对象状态转换为可保持或传输的形式的过程。序列化的补集是反序列化,后者将流转换为对象。这两个过程一起保证数据易于存储和传输。
MFC中对序列化的支持:
序列化是指将对象写入永久性存储媒体(如磁盘文件)或从其中读取对象的进程。MFC 对 CObject 类中的序列化提供内置支持。因此,所有从 CObject 派生的类都可利用 CObject 的序列化协议。
序列化的基本思想是对象应能将其当前状态(通常由该对象的成员变量指示)写入永久性存储中。以后,通过从存储中读取对象状态或反序列化对象状态,可以重新创建该对象。序列化处理序列化对象时使用的对象指针和对象循环引用的所有详细资料。关键之处在于对象本身负责读写其自身状态。因此,对于可序列化的类,必须实现基本的序列化操作。
MFC 将 CArchive 类的对象用作将被序列化的对象和存储媒体之间的中介物。该对象始终与 CFile 对象相关联,它从 CFile 对象获得序列化所需的信息,包括文件名和请求的操作是读取还是写入。执行序列化操作的对象可在不考虑存储媒体本质的情况下使用 CArchive 对象。
CArchive类对象为读写CFile类对象中的可序列化数据提供了一种安全的缓冲机制,它们之间形成了如下关系:
Serialize()函数 <==> CArchive类对象<==> CFile类对象<==>磁盘文件
CArchive 对象使用重载输出运算符 (<<) 和输入运算符 (>>) 来执行读写操作
综上所述,我们可以得知,要使用MFC序列化对一个文件实现读写,需要以下几个步骤:
1. 自定一个类 class A,该类继承自CObject类;
2. 在类的定义中使用DECLARE_SERIAL宏,在类的实现中使用IMPLEMENT_SERIAL宏;
3. 在自定义类A中,重载CObject类中的 Serialize()成员函数;
4. 创建一个文件,用于将自定义类对象存储到该文件中;
5. 构建一个CArchive对象,该对象与上述文件关联;
6. 通过上面个对象实现文件的读写。
一个使用MFC序列化机制对文件进行读写的过程:
1. 创建Student类,并且重写CObject类中的Serialize成员函数:
class CStudent : public CObject
{
DECLARE_SERIAL(CStudent)
CString name; //学生姓名
int age; //学生年龄
int grade; //学生年龄
public:
CStudent();
virtual ~CStudent();
void Serialize(CArchive &ar);
bool SetName(CString name);
CString GetName()const;
bool SetAge(int age);
int GetAge()const;
bool SetGrade(int grade);
int GetGrade()const;
};
其他成员函数相对简单,这里着重说明一下关于Seiralize函数的重写:
//重写CObject类的Serialize函数
void CStudent::Serialize(CArchive &ar)
{
if(ar.IsLoading()) //判断是否是输入流
{
ar>>this->name; //读入姓名
ar>>this->age; //读入年龄
ar>>this->grade; //读入年级
}
else
{
ar<<this->name; //读入姓名
ar<<this->age; //读入年龄
ar<<this->grade; //读入年级
}
}
2.建立对话框窗口并对相关空间绑定成员变量:
3.在录入按钮,和提取按钮中加入相关代码:
录入按钮代码:
void CserializeDlg::OnBnClickedButton1()
{
CStudent student; //创建CStudent对象
CString name;
this->ctrlInName.GetWindowTextW(name); //从控件中获取姓名
student.SetName(name);
CString age;
this->ctrlInAge.GetWindowTextW(age);
student.SetAge(atoi((char*)age.GetBuffer(age.GetLength()+1))); //从控件中获取年龄
CString grade;
this->ctrlInGrade.GetWindowTextW(grade);
student.SetGrade(atoi((char*)grade.GetBuffer(grade.GetLength()+1))); //从控件中获取年级
CFile file(L"1.txt",CFile::modeCreate|CFile::modeWrite); //输出文件
CArchive ar(&file,CArchive::store); //存储型
student.Serialize(ar); //输出该对象;
ar.Close(); //关闭输出流
file.Close(); //关闭文件
}
提取按钮代码:
void CserializeDlg::OnBnClickedButton2()
{
CStudent student; //构造一个对象
CFile file(L"1.txt",CFile::modeRead);
CArchive ar(&file,CArchive::load); //输入型
student.Serialize(ar); //读入对象
CString name = student.GetName();
this->ctrlOutputName.SetWindowTextW(name); //设置控件显示姓名
CString Age;
Age.Format(L"%d",student.GetAge());
this->ctrlOutputAge.SetWindowTextW(Age); //设置控件显示年龄
CString grade;
grade.Format(L"%d",student.GetGrade());
this->ctrlOutputGrade.SetWindowTextW(grade); //设置控件显示年级
ar.Close(); //关闭输入流
file.Close(); //关闭文件
}
4.写入一个学生信息:
5.查看文件中的数据:
6.重新提取已经写入的学生信息:
另外的一些话:
1.序列化对于文件的存储方式与其他的文件存储方式不兼容。
2.可以采用重载操作符:<< 和 >>直接对继承自CObject类的子类对象进行操作,但是直接操作只能作用于指针类型,非值型。
例如:
CStudent* pStudent;
pStudent = new CStudent;
···················
···················
ar<<student;
3. 请不要将 CArchive 类与通用 iostream 类混淆,iostream 类只用于格式化的文本。而 CArchive 类则用于二进制格式的序列化对象。