0---空指针 7FFF---大索引号标志,即后面的索引号是32位的
0X8000---保留以后使用 0XFFFF---新类的定义
小索引对象或者类的索引号:1~~~7FFE,但是类的最高位是1
对象的插入:writeobject函数:(全局插入<<函数只是调用了这个函数)首先插入类信息,然后是对象信息。每次写入(即一次writeobject函数的执行流程)是下面三种的之一:
1 若是未写过的类并且未被写过的对象:
1.1 写入新类标志:0XFFFF *this << wNewClassTag;
1.2 写入类的版本号,写入类名的字符串长度:ar << (WORD)m_wSchema << nLen;
1.3 写入类名 ar.Write(m_lpszClassName, nLen*sizeof(char));
1.4 写入对象 ((CObject*)pOb)->Serialize(*this);
2 若是曾经写过的类并且未写过的对象:
2.1 写入类的索引号 nClassIndex = (DWORD)(*m_pStoreMap)[(void*)pClassRef]
如果是小索引类:则写入类*this << (WORD)(wClassTag | nClassIndex);
如果是大索引类:则写入大索引号标志(7FFF)和32位的类索引号(最高位是1) *this << wBigObjectTag;
*this << (dwBigClassTag | nClassIndex);
2.2 写入对象 ((CObject*)pOb)->Serialize(*this);
3 若是曾经写过的类并且曾经写过的对象:
3.1 写入对象的索引号
如果是小索引对象:则直接写入索引号*this << (WORD)nObIndex;
如果是大索引对象:则写入大索引号标志和32位的对象索引号(最高位是0)
*this << wBigObjectTag;
*this << nObIndex;
以上3种情况的写入都是首先写入一个字,这个字的内容就代表了之后字节即类信息的意义或者可以只是一个对象的索引号(情况三),即是新类(字节为0xFFFF)还是已经定过的小索引类(索引号从0x8001—0xFFFE)又或者是已经定义过的大索引类以(字节0x7FFF后续两个字节为索引号)。
对于读取上面文件格式的信息并且区分出将要读取的是类还是对象,是索引还是对象数据,在readObject中
首先读取一个字如果是0XFFFF则显然对应的是第一种情况,此时可以容易地读取数据;如果第一个字的最高位是1的话,很显然此时对应的就是第二种情况,即该字是一个类的索引号,而且是小索引类;如果是0x7FFF则此时对应的就是第三种情况大索引号对象或者第二种情况大索引号类;如果最高位不是1则此时对应的也是第三种情况但是小索引对象;在区分了第一个字以后就可以容易地读取后面的数据。这样每次的readObject函数的调用都只是提取以往某次writeObject函数写入的数据。
对象的提取:ReadObject函数,因为在宏IMPLEMENT_SERIAL里定义的提取操作符只是简单地调用了这个函数。首先提取类信息,以便正确地动态生成对象,然后是对象信息。
PS:m_pStoreMap里即包含了已经序列化的类(CRuntimeClass)和对象的指针。
UINT CArchive::GetObjectSchema()函数只能调用一次,一般该函数在某个类(ar正在序列化的类)的Serialize函数里头调用,它返回读取的类的版本号。以下几行来自readObject:
UINT nSchemaSave = m_nObjectSchema;
m_nObjectSchema = nSchema;
pOb->Serialize(*this);
m_nObjectSchema = nSchemaSave;
一般地,也正是在serialize里头来处理各种版本的序列化。
FAQ:
1. 为什么可以定义全局的插入操作符,而提取操作符却要对每个类在宏里头声明?
插入操作的是在已知对象所有信息的情况下的操作,包括对象的类型和状态,类信息的写入使用CruntimeClass非静态成员函数Store来实现的,而GetCRuntime成员函数又是虚函数所以可以用指向COBJECT的指针来正确地获取,然后正确地调用STORE函数;而对于提取操作,将要提取出的对象的类型和状态都是未知,提取类信息主要是用CruntimeClass的静态成员LOAD来获取,该成员获得文件中类信息之后通过查找全局的类型链表可以唯一地确定一个CrutimeClass类型的静态对象,通过该对象的createObject函数可以构造出即将提取的对象类型,然后利用动态构造的对象的序列化函数就可以正确地提取出对象,因为
1.1 在宏定义的提取操作符中classname参数是无法用COBJECT类来替换,因为如果替换的话则在提取信息过程中即使出现错误,比如说提取出的类型根本就不是可序列化的但是如果继承自COBJECT的话仍然可以通过错误检查。