BDS 2006 C隐藏内存pipe理器冲突(类新/删除与AnsiString)
我现在使用BDS 2006 Turbo C ++很长一段时间了,我的一些更大的项目( CAD / CAM,3D gfx引擎和天文计算)偶尔会抛出一个exception(例如一次在3到12个月内每天24/7重用)。 广泛的debugging后,我发现这一点:
//code1: struct _s { int i; } // any struct _s *s=new _s[1024]; // dynamic allocation delete[] s; // free up memory
这个代码通常在模板里面,因为_s
也可以是类,所以delete[]
这个代码应该可以正常工作,但是delete[]
对于结构(类看起来不错)不能正常工作。 没有任何例外,内存被释放,但它以某种方式损害内存pipe理器分配表,并在此之后,任何新的分配可能是错误的(新可以创build重叠的分配已经分配的空间,甚至未分配的空间,因此偶尔的例外)
我发现,如果我添加空的析构函数比突然似乎一切OK
struct _s { int i; ~_s(){}; }
那么现在是怪异的一部分。 在我更新这个项目之后,我发现AnsiString
类也有不好的重新分配。 例如:
//code2: int i; _s *dat=new _s[1024]; AnsiString txt=""; // setting of dat for (i=0;i<1024;i++) txt+="bla bla bla\r\n"; // usage of dat delete[] dat;
在这个代码中, dat
包含一些有用的数据,然后是一些通过添加行创build的txt
string,所以txt
必须重新分配几次,有时dat
被txt
覆盖(即使它们没有重叠,我需要temp需要AnsiString
重新分配txt
与dat
重叠)
所以我的问题是:
- 我在code1,code2中做错了什么?
-
有没有办法避免
AnsiString
(重新)分配错误? (但仍然使用它)- 经过广泛的debugging(在发布问题2之后),我发现
AnsiString
不会导致问题。 只有在使用它们时才会发生。 真正的问题可能是在OpenGL客户端之间切换。 我有打开/保存对话框预览vectorgraphics。 如果我禁用这些VCL子窗口的OpenGL使用比AnsiString
内存pipe理错误完全消失。 我不是支持什么问题( MFC / VCL窗口之间的不兼容或更可能我在切换上下文中犯了一些错误,将进一步调查)。 关注OpenGL窗口是: - 主要的VCL Form + OpenGL在
Canvas
客户区内 - 主要的MFC子项打开/保存对话框停靠在
Canvas
客户区内的VCL Form + OpenGL
- 经过广泛的debugging(在发布问题2之后),我发现
PS
- 这些错误取决于
new/delete/delete[]
用法的数量而不是分配的大小 - code1和code2错误都是重复的(例如,有一个parsing器来加载复杂的ini文件,如果ini没有改变,错误发生在同一行)
- 我只在大型项目(普通的源代码> 1MB)上检测到这些错误,结合使用了
AnsiString
和带有内部dynamic分配的模板,但是它们也可能在简单的项目中,但是很less发生,所以我错过了它。 - 受感染的项目规格:
- win32 noinstall standalone(使用Win7sp1 x64,但在XPsp3 x32上performance相同)
- 如果使用GDI或OpenGl / GLSL ,则不计费
- 如果使用设备驱动程序DLL ,则不测量
- 没有OCX或非标准的VCL组件
- 没有DirectX
- 1字节alignment的编译/链接
- 不要使用RTL ,包或框架(独立的)
对不起英语/语法错误…任何帮助/结论/build议表示赞赏。
经过广泛的debugging,我很好地隔离了这个问题。 尝试调用已删除的指针的任何删除后,bds2006 Turbo C ++的内存pipe理已损坏。 例如:
BYTE *dat=new BYTE[10],*tmp=dat; delete[] dat; delete[] tmp;
这之后内存pipe理就不可靠了。 (“新”可以分配已经分配的空间)
当然两次删除同一个指针是程序员方面的错误,但是我发现了所有产生这个问题的真正原因(源代码中没有任何明显的错误),请参阅以下代码:
//--------------------------------------------------------------------------- class test { public: int siz; BYTE *dat; test() { siz=10; dat=new BYTE[siz]; } ~test() { delete[] dat; // <- add breakpoint here siz=0; dat=NULL; } test& operator = (const test& x) { int i; for (i=0;i<siz;i++) if (i<x.siz) dat[i]=x.dat[i]; for ( ;i<siz;i++) dat[i]=0; return *this; } }; //--------------------------------------------------------------------------- test get() { test a; return a; // here call a.~test(); } // here second call a.~test(); //--------------------------------------------------------------------------- void main() { get(); } //---------------------------------------------------------------------------
在函数中get()
被称为类的析构函数两次。 一次真正的一次,因为我忘了创build构造函数
test::test(test &x);
[Edit1]代码的进一步升级
好的,我已经改进了类和结构甚至模板的初始化代码,以修复更多的错误情况。 将此代码添加到任何结构/类/模板,如果需要,则可以添加function
T() {} T(T& a) { *this=a; } ~T() {} T* operator = (const T *a) { *this=*a; return this; } //T* operator = (const T &a) { ...copy... return this; }
-
T
是结构/类名称 - 只有当
T
使用dynamic分配时才需要最后一个运算符,如果没有使用分配,可以保持原样
这也解决了这样的其他编译器问题:
- bcc32中一个简单数组的初始化错误太多
如果有人有类似的问题,希望这有助于。
如果你需要debugging你的内存分配,也可以在c ++代码mmap
中追踪一个指针 。