在vc10和vc11中使用MFC进行递归序列化导致stackl溢出错误。此错误不会出现在vc9上。
您好,
我正在序列化类对象。这个类是链表。
------------- MyClass.h -----------
class MyClass : public CObject
{
protected:
MyClass *m_pNext;
int var1;
enum1 var2;
enum2 var3;
enum3 var4;
double[3] m_Normal;
public:
// Serialization
DECLARE_SERIAL_MICRO
void Serialize(CArchive& ar);
};
因此,MyClass的大小非常小,并且小于30-40字节。
------------- MyClass.cpp -----------
IMPLEMENT_SERIAL(MyClass , CObject, VERSIONABLE_SCHEMA | 6)
void MyClass::Serialize(CArchive& ar)
{
ar.SerializeClass(RUNTIME_CLASS(MyClass));
UINT objSch = ar.GetObjectSchema();
CObject::Serialize(ar);
if(ar.IsStoring())
{
//store all variables
......
//Store Next pointer
ar<< m_pNext;
}
else
{
//Retrive all variables
......
//Deserialize Next pointer
ar >> m_pNext; // This is recursive deserialization call
}
}
现在这里,如果MYClass链表大小超过~4000项限制。发生堆栈溢出错误。 此错误仅出现在vc10和vc11中。 对于相同的代码和数据大小,vc9不会出现堆栈溢出错误。
vc11分析数据: 在反序列化开始之前:@esp = 2285040 我把断点放在了seralization函数的开头。 对于每次迭代,MyClass堆栈增长了512个字节.myclass的大小不是那么多。我认为MFC在反序列化时内部使用堆栈来实现自己的目的。
大约4000件之后:@esp = 2283968。 此时堆栈utlizaton是2MB。因为堆栈完全耗尽,发生崩溃。
我们的exe的堆栈限制为2MB。
vc9分析数据: 在反序列化之前:@esp = 2282960 然后,对于每个Myclass堆栈增长336个字节,并在一次或两次递归调用后返回到原始值或接近原始值。 并在反序列化6000项后最后@esp = 2283968。 因此,使用的总堆栈仅为1000字节。 似乎MFC序列化行为从vc9更改为vc10 / 11,因此堆栈不会在重新调用的调用之间重置为较低的值。
请告诉我为什么会这样?
我已经为6000个项目链接了列表数据。我想反序列化这些项目。我可以使用任何非递归方法(不需要这种递归反序列化调用)来检索这个递归存储的数据吗?
等待你的回复。
此致 阿米特
PS-最近我修改了这个类来存储从链表项目中恢复的COBJLIst。
在反序列化时,我们会检索此列表并从中构建链接列表。
所以现在开始save和retrive将没有任何issu.Problem是,我试图在我们已经转储链表数据的地方检索旧版本数据。
答案 0 :(得分:0)
您的可序列化类应该只具有它应该序列化的内容,因此MyClass *m_pNext
不是一个好的候选者。此外,Serialize
需要不才能执行以下操作:
ar.SerializeClass(RUNTIME_CLASS(MyClass));
UINT objSch = ar.GetObjectSchema();
CObject::Serialize(ar);
应该为基类调用方法SerializeClass
,以便也应该序列化基类。在您的情况下,没有需要序列化的用户定义的基类。您正在序列化同一个类。删除这三行。
答案 1 :(得分:0)
我看不到你当前如何检测到你已经在反序列化中加载了所有列表。如果将m_pNext
指针值保存为其他字段,则可以将项目字段和整个列表的序列化分开,以避免这样的递归:
class MyClass
{
public:
void Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
for (MyClass* node = this; node != NULL; node = node->m_pNext)
{
node->SerializeFields(ar);
}
}
else
{
for (MyClass* node = this; node != NULL; node = node->m_pNext)
{
node->DeserializeFields(ar);
if (node->m_pNext)
{
// replace garbage with valid pointer if needed
node->m_pNext = new MyClass();
}
}
}
}
private:
void SerializeFields(CArchive& ar) { /* store all variables */ }
void DeserializeFields(CArchive& ar) { /* load all variables */ }