递归序列化导致vc10 / 11中的堆栈溢出错误,但不会导致vc9中的堆栈溢出错误

时间:2014-10-30 11:35:34

标签: c++ visual-c++ serialization recursion mfc

在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,因此堆栈不会在重新调用的调用之间重置为较低的值。

  1. 请告诉我为什么会这样?

  2. 我已经为6000个项目链接了列表数据。我想反序列化这些项目。我可以使用任何非递归方法(不需要这种递归反序列化调用)来检索这个递归存储的数据吗?

  3. 等待你的回复。

    此致 阿米特

    PS-最近我修改了这个类来存储从链表项目中恢复的COBJLIst。

    在反序列化时,我们会检索此列表并从中构建链接列表。

    所以现在开始save和retrive将没有任何issu.Problem是,我试图在我们已经转储链表数据的地方检索旧版本数据。

2 个答案:

答案 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 */ }