重载new/delete云算符来跟踪内存分配

5 篇文章 0 订阅

1.最近在阅读《C++编程思想》这本书,记录下其中的知识点
直接上代码了:
MemCheck.h


#ifndef MEMCHECK_H
#define MEMCHECK_H

#include<cstddef> //for size_t

//use the new operator(both scalar and array version)
void *operator new(std::size_t, const char*, long);
void *operator new[](std::size_t, const char*, long);
//void operator delete(void*, const char*, long); 
//void operator delete[](void*, const char*, long);
#define new new(__FILE__,__LINE__)
//#define delete delete(__FILE__,__LINE__)

extern bool traceFlag;
#define TRACE_ON() traceFlag=true
#define TRACE_OFF() traceFlag=false

extern bool activeFlag;
#define MEM_ON() activeFlag=true;
#define MEM_OFF() activeFlag=false;

#endif//MEMCHECK_H 

MemCheck.cpp


#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<cstddef>

using namespace std;
#undef new

//declare
bool traceFlag = true;
bool activeFlag = true;

namespace{

	//memory map entry type 
	struct Info{
		void* ptr;
		const char* file;
		long line;
	};

	//memory map data
	const size_t MAXPTRS = 10000u;
	Info memMap[MAXPTRS];
	size_t nptrs = 0;

	//searches the map for an address
	int findPtr(void *p){
		for (size_t i = 0; i < nptrs; ++i){
			if (memMap[i].ptr == p){
				return i;
			}
		}
		return -1;
	}

	void delPtr(void* p){
		int pos = findPtr(p);
		assert(pos >= 0);

		//remove pointer from map
		for (size_t i = pos; i < nptrs - 1; ++i){
			memMap[i] = memMap[i + 1];
		}
		--nptrs;
	}

	struct Sentinel{
		~Sentinel(){
			if (nptrs>0){
				printf("leaked memory at:\n");
				for (size_t i = 0; i < nptrs; ++i){
					printf("\t%p (file:%s,line %ld)\n", memMap[i].ptr, memMap[i].file, memMap[i].line);
				}
			}
			else{
				printf("No user memory leaks!\n");
			}
		}

	};

	Sentinel s;
}//end anonymous namespace

void* operator new(size_t siz, const char* file, long line){
	void *p = malloc(siz);
	if (activeFlag){
		if (nptrs == MAXPTRS){
			printf("memory map too small(increase MAXPTRS)\n");
			exit(1);
		}
		memMap[nptrs].ptr = p;
		memMap[nptrs].file = file;
		memMap[nptrs].line = line;
		nptrs++;
	}
	if (traceFlag){
		printf("Allocated %u bytes at address %p", siz, p);
		printf("(file:%s,line:%ld)\n", file, line);
	}
	return p;
}

void* operator new[](size_t siz, const char* file, long line){
	return operator new(siz, file, line);
}

void operator delete(void* p){  //重载delete只允许一个参数
	if (findPtr(p) >= 0){
		free(p);
		assert(nptrs > 0);
		//??? free过了,这里还能找到吗? free究竟做了什么?
		//free 释放了指针指向的内存,但是指针是还存在的,free内部仅仅只是将这段内存记为了可用,指向该内存的指针是个变量并不会变
		//这个例子说明了虽然指针指向的内存释放了,但是还是可以通过指针来查找;又进行试验确定,指针释放后不能进行对于这个指针的赋值操作
		//pTreeNode p1=(pTreeNode)malloc(sizeof(pTreeNode));
		//typedef struct TreeNode{
        //TreeNode* left = nullptr;
        //TreeNode* right = nullptr;
        //DataType data;
        //}*pTreeNode,*pTree;
		//    p1->data=100;
		//    pTreeNode p2=(pTreeNode)malloc(sizeof(pTreeNode));
		//    p2->data=200;
		//    pTreeNode p3=(pTreeNode)malloc(sizeof(pTreeNode));
		//    p3->data=300;
		//
		//    p2->left=p3;
		//    p1->left=p2;
		//    printf("p1=%d p2=%d p3=%d\n",p1->data,p2->data,p3->data);
		    free(p2); free掉的是指针指向的内存,虽然指针还是在的,但是不能用这个指针进行赋值操作了
		    p2= nullptr;
		    p2->left=p3;
		//    printf("p1=%d p2=%d p3=%d\n",p1->data,p2->data,p3->data);
		delPtr(p);
		if (traceFlag){
			printf("Deleted memory at address %p\n", p);
		}
	}
	else if (!p && activeFlag){
		printf("Attempt to delete unknow pointer:%p\n", p);
	}
}

void operator delete[](void* p){
	operator delete(p);
}

测试程序:

#if 1  //memcheck system test
#include<iostream>
#include<vector>
#include<cstring>
#include"MemCheck.h"//must appear last

using namespace std;
class Foo{
	char* s;
public:
	Foo(const char *s){
		this->s = new  char[strlen(s) + 1];
		strcpy_s(this->s, strlen(s) + 1, s);
	}
	~Foo(){ 
		delete[] s; 
	}

};

int main(){
	MEM_ON();
	cout << "hello" << endl;
	int *p = new int;
	delete p;

	//int *q = new int[3];
	//delete[] q;
	//int *r = nullptr;
	//delete r;
	
	//{
	//	Foo s("Goodbye");
	//}
	//
	MEM_OFF();

	system("pause");
}

#endif

这个例子中主要有两个问题:
问题1.下面的重载delete的代码片段中,先是free( p ),然后又调用delPtr(p)

void operator delete(void* p){  //重载delete只允许一个参数
	if (findPtr(p) >= 0){
		free(p);
		assert(nptrs > 0);
		delPtr(p);

但是问题是delPtr中用p指针进行了查找,已经释放了p,又用p来查找不会有问题吗?

void delPtr(void* p){
		int pos = findPtr(p);

为了解决这个问题,就要搞明白free究竟对指针做了什么【参考文献1】,从下面的源码中不难看出,仅仅只是退回了一个size(),然后将内存的使用情况标记为可用而已,指针p的值并没有改变,所以findPtr§当然不会有问题了

     void free(void *ptr) 
    { 
            struct mem_control_block *free; 
            free = ptr - sizeof(struct mem_control_block); 
            free->is_available = 1; 
            return; 
    }

问题2:
既然重载new可以传入调用发生的文件和所在行数,那么重载delete中能不能也加上这两个参数,打印出delete调用时所在的文件和行数呢?试了下好像不行,【参考文献3中】

先看一下重载new/delete的规则:
重载的operator new的参数个数任意,但第一个参数必须是size_t类型的,返回值必须是void*。重载operator delete只允许有一个参数,且是void*型。

参考文献:
1.C语言的malloc()和free()函数原理【整理】
2.malloc和free实现的原理
3.重载全局new/delete实现内存检测
4.重载delete时的那点事

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值