在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
本文为大便一箩筐的原创内容,转载请注明出处,谢谢:http://www.cnblogs.com/dbylk/ 最近在为公司的项目写内存泄漏定位工具,遇到一些关于C++构造与析构对象的问题,在此记录一下。 一、不要混用 new/delete 和 new[]/delete[]在默认情况下,也就是不存在 operator new 的重载时,new一个自定义类型 ClassA 的对象时,C++ 会先调用 malloc 来申请一块 sizeof(ClassA) 大小的内存(操作系统会记录这块内存的首地址与大小),然后调用 ClassA 的构造函数在这块内存上初始化对象。此时,new 关键字会返回 malloc 得到的地址。调用delete时,会首先执行 ClassA 的析构函数,再调用 free 释放 malloc 得到的指针。 而new[]则稍微复杂一点,当你调用 new ClassA[nCount] 申请一个对象个数为 nCount 的 ClassA 数组时,编译器(MSVC)会调用 malloc 申请一块大小为 sizeof(ClassA) * nCount + 4 的内存,多出来的 4 bytes 被放在 new[] 关键字返回地址 ptr 的前面,其中记录了数组中元素的个数。当调用 delete[] 删除数组时,会根据数组首地址前 4 bytes 中记录元素的个数来依次调用数组中对象的析构函数(每次指针偏移 sizeof(ClassA) 大小),再调用 free 释放指针 (ptr - 4)。 因此,混用 new/delete 和 new[]/delete[] 通常会导致内存访问崩溃。然后这里用了“通常”,也就是说在某些特定情况下,混用 new/delete 和 new[]/delete[] 是不会有任何影响的:
然而当项目代码一旦复杂起来,要分清什么时候上面两个条件能够成立就不是那么轻松的事了,因此最好的方法就是无论何时何地都不要混用 new/delete 和 new[]/delete[]。 二、不要 delete “void” 指针在整理公司项目代码的过程中,发现有很多地方出现了类似于下面形式的代码:
可能写过类似代码的同学会觉得这种写法并没有什么问题,事实也是如此,它能够正常工作,既不会产生内存泄漏,也不会运行报错。 但是,上面情况只能说是一种幸运的巧合,如果发生一些微小的改变,结果就会发生意想不到的变化:
细心的同学可能已经看出来了,由于 pBuffer 是 void 指针,delete pBuffer 时,并不会调用 StructA 的析构函数,而这导致了 string 的析构函数也没有被调用,最终产生的结果就是 string 中的字符串缓冲泄漏。 三、尽量不要手动调用析构函数看下面的代码,你能看出程序输出结果是什么吗?
我想很多同学会觉得答案是这个:
然而很不幸的,正确答案是这个:
因为在手动调用 pObject 的析构函数时,虽然 pObject 所指向的内存空间并没有被释放,但执行完 Derive 的析构函数后, pObject 所指向对象的虚函数表指针会从指向派生类 Derive 的虚函数表恢复为指向基类 Base 的虚函数表。 所以,尽量不要在自己写的函数中手动调用析构函数是一个好习惯。 然而上面提到了“尽量”,那就是说事情并没有那么绝对,C++ 支持手动调用析构函数,自然有它的道理:当你需要自己写内存管理器时,手动调用析构函数是必须的。 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论