刚写完一段代码,由于将很运行在移动设备上, 我决定先测试一下内存的使用量, 结果发现了很严重的内存泄漏, 在前前后后翻看了new 和delete并确认没有漏写的情况下, 泄露依然存在!调试后最终确认了问题是因为union的不当使用造成的, 下面开始还原现场:

 1 typedef struct DATA_
 2 {
 3     DATA_(int size = 10)
 4     {
 5         pVoid = new char[nSize];
 6         this->size = size;
 7     }
 8     virtual ~DATA_()
 9     {
10         if (pVoid)
11         {
12             delete pVoid;
13         }
14     }
15     int nSize;
16     void *pVoid;
17 }DATA, *LPDATA;
18 
19 struct STRUCT1
20 {
21     STRUCT1(int count)
22     {
23         pVoid = new DATA[count];
24         this->count = count;
25     }
26     virtual ~STRUCT1()
27     {
28         if (pVoid)
29         {
30             delete pVoid;
31         }
32     }
33     int count;
34     union
35     {
36         void *pVoid;
37         LPDATA pData;
38     }
39 }
40 
41 int main(int argc, char* argv[])
42 {
43     for( int i = 0; i < 1000; i++)
44     {
45         void *p = new STRUCT1( 10 );
46         delete p;
47     }
48     return 0;
49 }

在上面这段代码中,STRUCT1中包含了若干个DATA结构,DATA结构又申请了默认为10byte大小的内存,并且内存在对象析构的时候会用delete回收。乍一看这个代码貌似不会泄露内存,其实不然,待我分析:

在C++中构造函数与析构函数的调用是由编译器完成的,其中构造函数的调用一般是在对象空间开辟完以后(栈对象或者堆对象都是一样的),将参数压栈,this指针(对象的起始地址)放入eax寄存器(不同的编译器做法可能不同),然后跳到构造函数去执行。而析构函数的调用则是当对象内存被回收的时候被调用(栈对象是当代码执行到变量可见域外之前调用, 堆对象是在delete语句的位置进行调用)。

然而编译器并是那么智能的可以理解coder的意图,析构函数的调用是根据当前delete的指针类型来确定的,而(下面)这段代码却没有提供类型, 这导致了DATA_的析构函数将不会被调用,内存泄漏就在所难免了。

    virtual ~STRUCT1()
    {
        if (pVoid)
        {
            delete pVoid;//问题在这, 应该使用delete pData;
        }
    }

 

 

posted on 2012-09-25 02:25  zcmmwbd  阅读(717)  评论(0编辑  收藏  举报