C++重载new,以及实现检测内存泄露版本的new
在c++中,new作为一个操作符,也是可以被重载的,这个可能很多人比较陌生。在 Effective C++这本书中,专门提到了这方面的知识,看过此书,做一些总结,顺便在网上找到一些内容,实现一个可以检测内存泄露的内存分配机制(new delete)。
在铺叙重载new之前,先说一下new_handler, 如果读过 windows结构化异常,对这个机制应该并不陌生,在windows结构化异常中,当出现未捕捉的异常出现时候,有一个函数handler用于处理这种默认情况,同时这个函数是可以替换成自己写的版本。
new_handler也是一样,当调用new的时候,不能成功分配内存的时候,就会调用new_handler
声明形式如下:
namespace std
{
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) thow();
}
其中new_handler就是一个返回值和参数都是空的函数,set_new_handler()设置一个新的handler的同时,返回旧版本的handler。
当new分配空间失败的时候,就会调用这个函数,就好像求助的感觉一样,既然有求助的意味,所以new_handler一般要实现如下一些功能:
1. 让更多内存被使用, 即有可以释放内存的功能
2. 安装另一个new_handler,如果当前的handler无法得到更多内存,则要考虑更换handler,使得在下次调用的时候,用新的handler来处理内存释放。
3. 卸除new_handler,也就是将null传递给 set_new_handler方法,如果当前没有handler,那么new调用失败会抛出异常
4. 不返回,通常调用 abort, exit.
首先说一下重载new的必要性,
1. 用来检测运用上的错误,例如内存泄露,多次删除一个指针等,最后我们将给出一个实现检测内存泄露的new
2. 强化效能。因为自带的new函数是一个行为比较中庸的函数,对于大内存,小内存,多线程都考虑。所以自然效率会低一些,所以我们可以根据自己的需求制定new,例如在完全单线程的情况下不用考虑线程安全的问题。
3. 为了收集使用上的统计数据。在实现一个真正使用自己程序的new和delete之前,应该搜集一些程序使用动态内存的特点,例如大小,寿命等。这个时候可以重载new 来实现这些功能。
2.1 全局重载new这个提法主要是为了和class new 做区分而用的。
假设现在要实现这样功能的new,如果要分配内存是 size,实际分配内存要比size大两个字节,然后分别在分配好的内存前后多四个字节,存储一个特殊的内容,作为签名。 在delete的时候,可以检测者 多分配的,以判断程序运行期间,是否对对这段内存的前后进行非法访问(underruns 和 overruns)
#include "mem_check_new.h"#include <malloc.h>#include <stdio.h>//#include <iostream>//using namespace std;#ifdef _DEBUGnamespace {struct info //定义表中存储的数据,分配的指针值,文件名,行号{void *ptr;const char *file_name;long line;};info ptr_list[1024]; //内存分配表unsigned int ptrn = 0;//内存分配表项int find_ptr(void *p) //依据指针,查询记录这个分配的表项{for(unsigned int i = 0; i < ptrn; i++){if(ptr_list[i].ptr == p){return i;}}return -1;}void del_ptr(unsigned int i) //依据表项id,删除这个分配记录{while(i+1 < ptrn){ptr_list[i] = ptr_list[i+1];i++;}ptrn--;}class process_end //在程序退出前,检测是否有泄露{public:~process_end(){for(unsigned int i = 0; i < ptrn; i++){printf("file: %s, line: %d Memory leak\n", ptr_list[i].file_name, ptr_list[i].line);}}};process_end pe; //用一个全局对象,在程序退出前,析构这个对象,然后检测开始}//end of namespace non;//void* operator new(size_t size, const char *file_name,long line)void* operator new(size_t size, const char *file_name,long line) //重载new,每次分配内存,都放入表中记录下来{void *p = malloc(size);ptr_list[ptrn].ptr = p;ptr_list[ptrn].file_name = file_name;ptr_list[ptrn].line = line;ptrn++;return p;}void *operator new[](size_t size, const char *file_name, long line) //重载 new[]{return operator new(size, file_name, line);}void operator delete(void *p) //调用delete的时候,首先在表中查找当前指针的值,如果存在就删除{int i = find_ptr(p);if(i >= 0){free(p);del_ptr(i);}else{printf("delete unknown pointer\n");}}void operator delete[](void *p){operator delete(p);}#endif