首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

内存泄露-关于内置对象和类对象的delete与delete【】有关问题

2012-03-29 
内存泄露----关于内置对象和类对象的delete与delete【】问题#include stringusing namespace stdint main

内存泄露----关于内置对象和类对象的delete与delete【】问题
#include <string>
using namespace std;

int main()
{
  int *i_p = new int[5];
  delete i_p; //1
 // delete []i_p; //2

  string *s_p = new string[5];
 // delete s_p; //3
  delete []s_p; //4

return 0;
}
 /**症状:*/
上述代码,在vc6环境,debug运行下,第1与2行的效果是一样的,通过检测,均没有造成内存泄露;而第3与4行的效果则大相径庭。采用第3行,编译连接通过,但执行出错。第4行为正解。

 /**试问:*/
对于内建类型,用delete【】和delete效果相同,系统会自动删除所有;而对于类类型,二者不同可能引起内存泄露。请问编译器在
此问题上如何处理的,它做了些什么?

 /**附注:*/
上述所谓检测手段,分别采用了5种方法来检测,症状均类似。其中包括MSDN中提到的“#ifdef _DEBUG
CMemoryState oldMem, newMem, diffMem;  
  oldMem.Checkpoint();  
  ......
  newMem.Checkpoint();
  diffMem.Difference( oldMem, newMem )
  ......”
 

[解决办法]

探讨
引用:
可能是这样吧,new/delete对内置类型和类的处理方式不一样
因为以前重载过new[],它返回的指针和分配的指针是差了4个字节的,好像
那四个字节是记录个数的,但是对于内置数据类型就不是
所以不会异常,因为为win32堆释放内存的时候和分配的时候指针值是一样的
但对于string,就不一样了,差了4个字节,所以会失败


就目前我所知道的信息就是,对于类类型对象,其所获取的内存一般情况…

[解决办法]
也没什么可谢的,呵呵,论坛就是大家讨论问题的,一切都是为了把问题搞清楚
lz还是不明白的话,再给你写一个例子,呵呵
注意一下输出内容和异常发生的位置(有两处会有异常,自己调试一下吧)
我觉得该明白了,至少我明白了

C/C++ code
class A{public:    A()    {        printf("Constr A\n");    }    ~A()    {        printf("Destr A\n");    }};class B{public:    B()    {        printf("Constr B\n");    }    ~B()    {        printf("Destr B\n");    }};class C{public:    C()    {        printf("Constr C\n");    }};int main(int argc, char* argv[]){    A *a=new A[2];    B *b=(B*)a;    delete [] b;    C *c=new C[2];    a=(A*)c;    delete a;    c=new C[2];    delete [] c;    c=new C[2];    delete c;    c=new C[2];    a=(A*)c;    delete [] a;    a=new A[2];    c=(C*)a;    delete [] c;    return 0;}
[解决办法]
classTwo
{
inta;
public:
Two( int b = 0){a = b;};
~Two( void ){a= 0;};
};


int main(int argc, char* argv[])
{
char *p = new char;
char *p1= new char[10];
Two *p2 = new Two( 10 );
Two *p3 = new Two[20];

p1[0] = *p;

delete p;
delete p1;
delete p2;
delete p3;
delete []p3;
return 0;
}
汇编代码:
0041EF30 push ebp
0041EF31 mov ebp,esp
0041EF33 push 0FFh
0041EF35 push offset __ehhandler$_main (00426f36)
0041EF3A mov eax,fs:[00000000]
0041EF40 push eax
0041EF41 mov dword ptr fs:[0],esp
0041EF48 sub esp,9Ch
0041EF4E push ebx
0041EF4F push esi
0041EF50 push edi
0041EF51 lea edi,[ebp-0A8h]
0041EF57 mov ecx,27h
0041EF5C mov eax,0CCCCCCCCh
0041EF61 rep stos dword ptr [edi]
16: char *p = new char;
0041EF63 push 1
0041EF65 call operator new (0040ec40)
0041EF6A add esp,4
0041EF6D mov dword ptr [ebp-20h],eax
0041EF70 mov eax,dword ptr [ebp-20h]
0041EF73 mov dword ptr [ebp-10h],eax
17: char *p1= new char[10];
0041EF76 push 0Ah
0041EF78 call operator new (0040ec40) 调用new
0041EF7D add esp,4
0041EF80 mov dword ptr [ebp-24h],eax
0041EF83 mov ecx,dword ptr [ebp-24h]
0041EF86 mov dword ptr [ebp-14h],ecx


18: Two *p2 = new Two( 10 );
0041EF89 push 4
0041EF8B call operator new (0040ec40) 调用new
0041EF90 add esp,4
0041EF93 mov dword ptr [ebp-2Ch],eax
0041EF96 mov dword ptr [ebp-4],0
0041EF9D cmp dword ptr [ebp-2Ch],0
0041EFA1 je main+82h (0041efb2)
0041EFA3 push 0Ah
0041EFA5 mov ecx,dword ptr [ebp-2Ch]
0041EFA8 call @ILT+510(Two::Two) (00401203) 调用构造器
0041EFAD mov dword ptr [ebp-58h],eax
0041EFB0 jmp main+89h (0041efb9)
0041EFB2 mov dword ptr [ebp-58h],0
0041EFB9 mov edx,dword ptr [ebp-58h]
0041EFBC mov dword ptr [ebp-28h],edx
0041EFBF mov dword ptr [ebp-4],0FFFFFFFFh
0041EFC6 mov eax,dword ptr [ebp-28h]
0041EFC9 mov dword ptr [ebp-18h],eax
19: Two *p3 = new Two[20];
0041EFCC push 54h
0041EFCE call operator new (0040ec40)
0041EFD3 add esp,4
0041EFD6 mov dword ptr [ebp-34h],eax
0041EFD9 mov dword ptr [ebp-4],1
0041EFE0 cmp dword ptr [ebp-34h],0
0041EFE4 je main+0E4h (0041f014)
0041EFE6 push offset @ILT+525(Two::~Two) (00401212) 保存了析构地址
0041EFEB push offset @ILT+505(Two::`default constructor closure') (004011fe)
0041EFF0 mov ecx,dword ptr [ebp-34h]
0041EFF3 mov dword ptr [ecx],14h
0041EFF9 push 14h
0041EFFB push 4
0041EFFD mov edx,dword ptr [ebp-34h]
0041F000 add edx,4
0041F003 push edx
0041F004 call `eh vector constructor iterator' (0041f1a0) 调用矢量构造迭代器
0041F009 mov eax,dword ptr [ebp-34h]
0041F00C add eax,4
0041F00F mov dword ptr [ebp-5Ch],eax
0041F012 jmp main+0EBh (0041f01b)
0041F014 mov dword ptr [ebp-5Ch],0
0041F01B mov ecx,dword ptr [ebp-5Ch]
0041F01E mov dword ptr [ebp-30h],ecx
0041F021 mov dword ptr [ebp-4],0FFFFFFFFh
0041F028 mov edx,dword ptr [ebp-30h]
0041F02B mov dword ptr [ebp-1Ch],edx
20:
21: p1[0] = *p;
0041F02E mov eax,dword ptr [ebp-14h]
0041F031 mov ecx,dword ptr [ebp-10h]
0041F034 mov dl,byte ptr [ecx]
0041F036 mov byte ptr [eax],dl
22:
23: delete p;
0041F038 mov eax,dword ptr [ebp-10h]
0041F03B mov dword ptr [ebp-38h],eax
0041F03E mov ecx,dword ptr [ebp-38h]
0041F041 push ecx
0041F042 call operator delete (00409f20) 调用delete
0041F047 add esp,4
24: delete p1;
0041F04A mov edx,dword ptr [ebp-14h]
0041F04D mov dword ptr [ebp-3Ch],edx
0041F050 mov eax,dword ptr [ebp-3Ch]
0041F053 push eax
0041F054 call operator delete (00409f20) 调用delete
0041F059 add esp,4
25: delete p2;
0041F05C mov ecx,dword ptr [ebp-18h]
0041F05F mov dword ptr [ebp-44h],ecx
0041F062 mov edx,dword ptr [ebp-44h]
0041F065 mov dword ptr [ebp-40h],edx
0041F068 cmp dword ptr [ebp-40h],0
0041F06C je main+14Dh (0041f07d)
0041F06E push 1
0041F070 mov ecx,dword ptr [ebp-40h]
0041F073 call @ILT+530(Two::`scalar deleting destructor') (00401217) 调用标量析构器
0041F078 mov dword ptr [ebp-60h],eax
0041F07B jmp main+154h (0041f084)
0041F07D mov dword ptr [ebp-60h],0
26: delete p3;
0041F084 mov eax,dword ptr [ebp-1Ch]
0041F087 mov dword ptr [ebp-4Ch],eax
0041F08A mov ecx,dword ptr [ebp-4Ch]
0041F08D mov dword ptr [ebp-48h],ecx
0041F090 cmp dword ptr [ebp-48h],0


0041F094 je main+175h (0041f0a5)
0041F096 push 1
0041F098 mov ecx,dword ptr [ebp-48h]
0041F09B call @ILT+530(Two::`scalar deleting destructor') (00401217) 调用标量析构器
0041F0A0 mov dword ptr [ebp-64h],eax
0041F0A3 jmp main+17Ch (0041f0ac)
0041F0A5 mov dword ptr [ebp-64h],0
27: delete []p3;
0041F0AC mov edx,dword ptr [ebp-1Ch]
0041F0AF mov dword ptr [ebp-54h],edx
0041F0B2 mov eax,dword ptr [ebp-54h]
0041F0B5 mov dword ptr [ebp-50h],eax
0041F0B8 cmp dword ptr [ebp-50h],0
0041F0BC je main+19Dh (0041f0cd)
0041F0BE push 3
0041F0C0 mov ecx,dword ptr [ebp-50h]
0041F0C3 call @ILT+535(Two::`vector deleting destructor') (0040121c) 调用矢量析构器
0041F0C8 mov dword ptr [ebp-68h],eax
0041F0CB jmp main+1A4h (0041f0d4)
0041F0CD mov dword ptr [ebp-68h],0
28: return 0;
0041F0D4 xor eax,eax
29: }
0041F0D6 mov ecx,dword ptr [ebp-0Ch]
0041F0D9 mov dword ptr fs:[0],ecx
0041F0E0 pop edi
0041F0E1 pop esi
0041F0E2 pop ebx
0041F0E3 add esp,0A8h
0041F0E9 cmp ebp,esp
0041F0EB call __chkesp (0040bfc0)
0041F0F0 mov esp,ebp
0041F0F2 pop ebp
0041F0F3 ret

[解决办法]
现在市面上能找到的书上是这样记载的,
int *a = new int[NUM];
delete a;
只有 第一个元素会被释放。

但是 跟进微软的代码可以看见,


void operator delete(
void *pUserData
)
{
_CrtMemBlockHeader * pHead;

RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));

if (pUserData == NULL)
return;

_mlock(_HEAP_LOCK); /* block other threads */
__TRY

/* get a pointer to memory block header */
pHead = pHdr(pUserData); //注意这一句!!!!
 
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));

_free_dbg( pUserData, pHead->nBlockUse );

__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY

return;
}
上面 pHead = pHdr(pUserData); 直接将一个内存地址转换成了_CrtMemBlockHeader*类型,而 这个结构体是这样定义的 
typedef struct _CrtMemBlockHeader
{
struct _CrtMemBlockHeader * pBlockHeaderNext;
struct _CrtMemBlockHeader * pBlockHeaderPrev;
char * szFileName;
int nLine;
#ifdef _WIN64
/* These items are reversed on Win64 to eliminate gaps in the struct
* and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
* maintained in the debug heap.
*/
int nBlockUse;
size_t nDataSize; //注意 这一句!!!
#else /* _WIN64 */
size_t nDataSize;
int nBlockUse;
#endif /* _WIN64 */
long lRequest;
unsigned char gap[nNoMansLandSize];
/* followed by:
* unsigned char data[nDataSize];
* unsigned char anotherGap[nNoMansLandSize];
*/
} _CrtMemBlockHeader;

size_t nDataSize; 可以明确的记录你分配内存的大小,我跟过代码。确实很准。
我想这就是为什么内建型不会报错也不会内存泄露的原因。微软在new的时候就已经记录了数据大小。以释放时使用。
而为什么在释放自创型别的时候会报错,我想应该是由于程序简单的调用了free而没有析构过程。

呵呵,先猜想,再看看各位的答案。

热点排行