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时的那点事