C++ 新对象导致堆损坏
几天来,我一直在与堆损坏问题作斗争。vs 2005调试器首先警告我,在删除以前新建的对象后,我可能损坏了堆。对此问题的研究导致我使用gflags和页面堆设置。为我的特定图像启用此设置后,它可能会将我指向实际导致损坏的行 Gflags将问题对象的构造函数确定为罪魁祸首。对象派生如下:C++ 新对象导致堆损坏,c++,heap,gflags,C++,Heap,Gflags,几天来,我一直在与堆损坏问题作斗争。vs 2005调试器首先警告我,在删除以前新建的对象后,我可能损坏了堆。对此问题的研究导致我使用gflags和页面堆设置。为我的特定图像启用此设置后,它可能会将我指向实际导致损坏的行 Gflags将问题对象的构造函数确定为罪魁祸首。对象派生如下: class POPUPS_EXPORT MLUNumber : public MLUBase { ... } class POPUPS_EXPORT MLUBase : public BusinessLog
class POPUPS_EXPORT MLUNumber : public MLUBase
{
...
}
class POPUPS_EXPORT MLUBase : public BusinessLogicUnit
{
...
}
我可以在单独的线程中实例化一个MLUNumber,并且不会发生堆损坏
我可以实例化另一个类,该类也继承自MLUBase,不会导致堆损坏
访问冲突是由于构造函数的左大括号上发生损坏而引发的,这似乎是由于隐式初始化对象(?)
基类构造函数(MLUBase)成功完成
从VS2005中的内存窗口中可以看出,似乎没有为实际对象分配足够的空间。我的猜测是,只为基类分配了足够的资源
导致故障的线路:
BusinessLogicUnit* biz = new MLUNumber();
我希望找到一个可能导致这种情况的原因,或者接下来是另一个故障排除步骤 BusinessLogicUnit不是MLUNumber。你为什么要这样分配?相反
BusinessLogicUnit*biz=新的BusinessLogicUnit()
如何删除内存?使用基类指针?您是否已将BusinessLogicUnit
的析构函数设置为虚拟?它必须是虚拟的
class BusinessLogicUnit
{
public:
//..
virtual ~BusinessLogicUnit(); //it must be virtual!
};
<> P>通过基类指针删除派生类对象,调用C++标准的未定义行为。 < P>不幸的是,由于所给出的信息,无法确定问题。 您可能需要检查以下内容:
- 确保BusinessLogicUnit具有虚拟析构函数。当通过基指针删除对象时,基类中必须存在虚拟析构函数,子类才能正确析构函数
- 确保生成的所有源文件都具有相同的预处理器标志和编译器选项。标志的差异(可能是调试/发布标志之间的差异?)可能会导致结构大小的变化,从而导致不同源文件中报告的大小不一致
- 即使使用gflags设置,某些类型的堆损坏也可能未被检测到。审核您的其他堆使用情况,以尝试找到问题的根源。理想情况下,您应该将可靠崩溃的最小测试用例放在一起,但活动量最少,这样您就可以缩小导致崩溃的原因
- 尝试一个干净的解决方案并重建;我偶尔会看到时间戳被搞砸,一个旧的对象文件可能会使用过时的结构定义。至少值得检查:)
- 或者你做了类似的事情
struct A
{
SomeType & m_param;
A(SomeType & param) : m_param(param)
{
...use m_param here...
}
};
A a(SomeType()); // passing a temporary by reference
那么这就是未定义的行为,因为引用的临时文件在
m_param(param)
发生后立即死亡。我同意bdonlan的观点,即没有足够的信息来找出问题所在。这里有很多好的建议,但仅仅猜测应用程序崩溃的可能原因并不是解决问题的明智方法
通过启用检测(pageheap)来帮助您缩小问题范围,您做了正确的事情。我将继续沿着这条路走下去,确切地找出导致访问冲突的内存地址(以及地址的来源)。能否请您发布一个可以重现问题的代码的最小示例?根据您提供的信息,我们所能做的只是提供一些有根据的猜测。如果您不能仅通过在VS2005调试器中运行调试版本的代码来解决这个问题,我会非常惊讶。这将启用额外的堆检查,该检查应在错误发生时而不是在以后的随机时间检测错误。是否启用了完整的pagheap?我已通过gflags.exe启用了完整的页面堆。我在vs2005中找不到启用它的设置。目前,仅vs2005(没有gflags)在我销毁对象时会警告我以前损坏了堆。创建对象不会导致堆损坏。但是,它通常可以检测在程序执行过程中的任何时候可能发生的堆损坏。关于损坏的数据,你不能判断它是否损坏,除非你做一些依赖于它是否损坏的事情。这是一个完全合理的做法-BusinessLogicUnit可以是一个抽象接口类。只要析构函数是虚拟的,它本身就完全安全。不,它不是:MLUNumber不在BusinessLogicUnit的层次结构中。星期五晚上:我一定不能醒着。@John,那不是真的-它是。这真的只是为了调试的理智。最初的调用是SomeObject->Switch(new-bluecure);。我已经确认MLUNumber*biz5=新的MLUNumber();也会导致崩溃。是:virtual~BusinessLogicUnit(void);请记住,在构建时而不是销毁时会引发访问冲突(由于gflags和页面堆设置)。这让我认为构造函数是罪魁祸首。所有的析构函数都是虚拟的。我将检查预处理器标志。不过,我肯定在调试所有版本。我不知道如何更好地测试堆损坏。我每次都能重现,但不知道是什么原因造成的。我已经尝试过干净的解决方案,我正在交叉手指:)@reuscam,这里的关键字是“minimal”-开始删除代码,看看它是否还能复制。最后,希望你能找到问题所在。希望如此。最后的答案是dll和从dll中提取头的exe之间的字节对齐方式不同。这真的来了
struct A
{
SomeType & m_param;
A(SomeType & param) : m_param(param)
{
...use m_param here...
}
};
A a(SomeType()); // passing a temporary by reference