代码之家  ›  专栏  ›  技术社区  ›  Rhubbarb

为什么向量删除析构函数作为标量删除的结果被调用?

  •  11
  • Rhubbarb  · 技术社区  · 14 年前

    我已经删除了很多细节,因为我试图把它归结为赤裸裸的骨头; 我不认为这遗漏了什么重要的东西。

    // in a DLL:
    
    #ifdef _DLL
    #define DLLEXP __declspec(dllexport)
    #else
    #define DLLEXP __declspec(dllimport)
    #endif
    
    class DLLEXP MyClass // base class; virtual
    {
    public:
      MyClass() {};
      virtual ~MyClass() {};
    
      some_method () = 0; // pure virtual
    
      // no member data
    };
    
    class DLLEXP MyClassImp : public MyClass
    {
    public:
      MyClassImp( some_parameters )
      { 
        // some assignments...
      }
    
      virtual ~MyClassImp() {};
    
    private:
      // some member data...
    };
    

    以及:

    // in the EXE:
    
    MyClassImp* myObj = new MyClassImp ( some_arguments ); // scalar new
    // ... and literally next (as part of my cutting-down)...
    delete myObj; // scalar delete
    

    在Visual Studio(2008 Pro)中的调试生成中, 在微软<dbgheap.c>, 以下断言失败:

    _ASSERTE(_CrtIsValidHeapPointer(pUserData));
    

    mydll_d.dll!operator delete()
    mydll_d.dll!MyClassImp::`vector deleting destructor'()
    

    我想这应该是

    mydll_d.dll!MyClassImp::`scalar deleting destructor'()
    

    MyClassImp* myObj = new MyClassImp ( some_arguments );
    delete[] newObj; // array delete
    

    中的地址 pUserData 那是你的吗 myObj 自身(与成员相对)。 该地址周围的内存如下所示:

                                    ... FD FD FD FD
    (address here)
    VV VV VV VV MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
    FD FD FD FD AB AB AB AB AB AB AB AB EE FE EE FE
    ...
    

    四个人在哪里 VV s大概是虚拟函数表的地址, MM...MM 是可识别的成员数据, 其他字节是调试器放置的各种特殊标记 FD FD 是对象存储器周围的“保护字节”)。

    VV型 如果有变化,

    这不是问题所在;我的毁灭者都是虚拟的。

    我注意到微软的网页 “错误:为导出类调用了错误的运算符Delete” http://support.microsoft.com/kb/122675

    在我的例子中,删除析构函数的“味道”似乎是错误的:

    我正在努力产生最小的削减代码的过程中,仍然表现出问题。

    然而,任何提示或提示,以帮助如何进一步调查这个问题将不胜感激。

    也许这里最大的线索是 mydll_d.dll!operator delete() 在堆栈上。 我应该期待这样吗 myexe_d.exe!operator delete() , DLLEXP 谁“迷路了”?

    有没有一个好的参考,我可以阅读关于什么 _CrtIsValidHeapPointer 检查?

    5 回复  |  直到 14 年前
        1
  •  8
  •   bshields    14 年前

    听起来这可能是一个问题,即从一个堆中进行分配,然后尝试在另一个堆中进行删除。当从dll分配对象时,这可能是一个问题,因为dll有自己的堆。从你展示的代码来看,这似乎不是问题所在,但在简化过程中,可能丢失了一些东西?在过去,我看到这样的代码使用工厂函数和虚拟函数 destroy 方法来确保在dll代码中进行分配和删除。

        2
  •  1
  •   Josh Kelley    14 年前

    微软为他们的C运行时提供了源代码;你可以去看看是什么 _CrtIsValidHeapPointer C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src\dbgheap.c .

    delete newObj; // scalar delete
    

    delete[] newObj;
    

    delete pointerToClassLikeMyClassThatIsInExeAndNotDll;
    

    来检验你关于 delete[] 被召唤。类似地,您可以检查调用堆栈

    删除指针ToClassLikeMyClassThatIsNexeandNotDll;
    

    mydll_d.dll!operator delete() myexe_d.exe!operator delete() .

        3
  •  1
  •   Rhubbarb    14 年前

    谢谢你的回答和评论。 所有这些都是有用和相关的。


    一旦你开始从DLL导出类, 在我看来像是/MT。

    我发现了一个/MT和/MTd的“掩埋”实例,应该是/MD和/MDd, 加上其他设置中一些相关的不一致。

    纠正这些错误之后, 现在不会抛出任何断言,代码的行为似乎是正确的。


    以下是在执行时遇到崩溃或断言失败时要检查的一些内容:调用作用域和析构函数。 在所有配置中(尤其是在有问题的配置中):

    (此处*.vcproj路径相对于</VisualStudioProject/Configurations/Configuration/>)

    • 在中选择了正确的运行时 C/C++代码生成运行库 <工具[@Name=“vcclccompilertool”]/@RuntimeLibrary>;
    • 适当的定义(如有)在 <工具[@Name=“vcclccompilertool”]/@PreprocessorDefinitions> 特别是关于静电的使用 (例如,STLP使用静态库与STLport使用动态库);
    • 在中选择适当版本的库 链接器|输入|其他依赖项 <工具[@Name=“vLinkerTool”]/@AdditionalDependencies> 特别是关于静态运行库的 (例如stlport\u static.lib与stlport。 .lib)。

    有趣的是 标量 矢量 正在删除析构函数。 因此,这可能是一个'红鲱鱼'。

    或许我还错过了其他一些微妙之处。

        4
  •  1
  •   Ingo Löhken    14 年前

    这种行为对于MSVC 9是特殊的,在MSVC 9中,导出类的delete操作符(具有虚拟析构函数)隐式生成并损坏为带有相关标志的vector dtor,其中1表示(标量),3表示(向量)。

    此外,向量dtor似乎也执行错误,如果新的被分配到实现所在的模块之外的另一个模块中,然后通过引用计数被保存在静态变量中,该引用计数在进程关闭时执行delete this(这里向量dtor起作用)。

    这与前面提到的堆问题“bshiels”相匹配,dtor在错误的堆上执行,代码在关闭时出现“无法读取该内存位置”或“访问冲突”的情况—此类问题似乎非常常见。

    解决这个bug的唯一方法是禁止使用虚拟析构函数,并自己执行它,通过强制使用delete函数从基类中删除这个函数-尽管很愚蠢,但你还是模仿了虚拟dtor应该为你做的事情。然后执行标量dtor,在关闭时,模块之间共享的任何ref计数对象都可以以安全的方式实例化,因为堆总是正确地寻址到源模块。

    要检查,如果您有这样的问题,只需禁止使用vector delete操作符。

        5
  •  0
  •   Community CDub    7 年前

    在我的例子中,删除析构函数的“味道”是错误的

    这不是问题所在。根据中的伪代码 Mismatching scalar and vector new and delete ,的 scalar deleting destructor 只需呼叫 vector deleting descructor 有个旗子写着“做标量破坏而不是向量破坏”。

    正如其他海报所指出的,您的实际问题是在一个堆上进行分配,而在另一个堆上进行删除。最清晰的解决方案是给类提供 operator new operator delete Error deleting std::vector in a DLL using the PIMPL idiom