总所周知,C++是一个不支持垃圾自动回收的编程语言,在实际编程中C++常常因为内存泄漏而引发的问题不在少数。但这真的是C++的缺点吗?
首先,我们来看看Java的自动垃圾回收。Java使用多线程技术,在后台有一垃圾回收线程,有最低的中断值,因此只能在其他线程休息时才开始执行。但它并不是在其他线程休息时就一定能执行,激发它执行的有两个条件:一是程序可分配内存达到最大值,可能是系统内存资源不足等情况,此时垃圾回收线程才被激活;二是代码中执行 System.gc() 函数,而执行这个函数并不代表开始内存回收了,而是通知Java虚拟机准备开始内存回收。
C#的托管垃圾回收虽然从性能上比Java要好,但任何垃圾回收机制都会存在一个内存浪费与CPU额外开销的问题。
现在,我们来讨论一下关于C++内存泄漏的避免的问题,可以试C++程序从根本上避免内存泄漏。
如果在一个庞大的C++程序中,如果检测出有严重的内存泄漏,如果查找所有的 new、delete 并跟踪是一种十分不明智的选择。C++是一个非常灵活的编程语言,运算符重载就是其灵活性的最佳体现。全局运算符重载甚至可以重载 new、delete 等运算符。
下面给出 MemoryTrack.h 源代码:
#include <Windows.h>
#include <stdio.h>
#ifdef _DEBUG
//#define USE_MULTITHREADING //如果是多线程应用程序则将这句取消注释
#pragma comment(linker, "/subsystem:console")//设置编译模式为控制台程序
int iTrack = 0;//累计未释放的分配的个数
#ifdef USE_MULTITHREADING
char* cMutexName = "MemoryTrack_Test";//互斥量名称
#endif
void* operator new(unsigned int size, const char *file, int line) {//重载new运算符
#ifdef USE_MULTITHREADING
//等待其他重载执行完毕
HANDLE hMutex;
do {
hMutex = CreateMutexA(NULL, true, cMutexName);
} while (ERROR_ALREADY_EXISTS == GetLastError());
#endif
//设置跟踪的数据并打印数据
printf("程序在%s文件的%d行new了一块内存。\r\n当前累计未释放的内存块有%d个。\r\n\r\n",
file, line, ++iTrack);
#ifdef USE_MULTITHREADING
//关闭互斥量
ReleaseMutex(hMutex);
#endif
//分配内存空间
return malloc(size);
}
void operator delete(void* p) {
#ifdef USE_MULTITHREADING
//等待其他重载执行完毕
HANDLE hMutex;
do {
hMutex = CreateMutexA(NULL, true, cMutexName);
} while (ERROR_ALREADY_EXISTS == GetLastError());
#endif
//设置跟踪的数据并打印数据
printf("程序delete了一块内存。\r\n当前累计未释放的内存块有%d个。\r\n\r\n", --iTrack);
//释放内存空间
free(p);
#ifdef USE_MULTITHREADING
//关闭互斥量
ReleaseMutex(hMutex);
#endif
}
void operator delete[](void* p) {
#ifdef USE_MULTITHREADING
//等待其他重载执行完毕
HANDLE hMutex;
do {
hMutex = CreateMutexA(NULL, true, cMutexName);
} while (ERROR_ALREADY_EXISTS == GetLastError());
#endif
//设置跟踪的数据并打印数据
printf("程序delete了一块内存。\r\n当前累计未释放的内存块有%d个。\r\n\r\n", --iTrack);
//释放内存空间
free(p);
#ifdef USE_MULTITHREADING
//关闭互斥量
ReleaseMutex(hMutex);
#endif
}
//设置全局重载重定义,用于跟踪内存分配的位置
#define new new(__FILE__, __LINE__)
#endif
在C++项目中,只需要在项目中引用这个头文件即可实现跟踪程序所有 new、delete 操作。另外可能导致错误的情况是头文件中具有堆变量的定义,可能在多文件项目中导致链接错误发生。此时只需要将变量声明为类静态变量并在一个.cpp文件中声明类静态变量的存储空间,即可解决这类问题。另外,这个头文件封装了多线程相关操作。如果需要检测内存泄漏的程序为多线程程序,则只需要在头文件前面加上“#define USE_MULTITHREADING”即可。
如图所示,内存的分配与释放一目了然,在大型程序中也为内存泄漏的寻找提供了强大的方法。从效率上讲,有了运算符重载,C++不支持自动垃圾回收岂不更好?