调试技术
1 为了对数组边界进行检查 当准备发行代码时,可以关闭边界检查
提高性能
2 检查基类中的非虚析构函数
通常的内存分配问题包括:对不是在动态存储区 free store上分配的
内存误使用delete,多次重复释放在动态存储区上分配一个内存,最常见
的情况是忘记删除一个指针
为了使用这种内存检查系统,在这里只需包含头文件MemCheck.h,
并链接MemCheck.obj到应用程序中,这个系统能够截获所有对new
和delete的调用,并且通过在程序中调用宏MEM_ON()来初始化内存跟踪
有多个参数的operator new()来实现,当new被调用的时候,用_FILE_
和_LINE_宏来获得其所在的文件名和行号并存储
//: C02:MemCheck.h
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
#ifndef MEMCHECK_H
#define MEMCHECK_H
#include <cstddef> // For size_t
// Usurp the new operator (both scalar and array versions)
void* operator new(std::size_t, const char*, long);
void* operator new[](std::size_t, const char*, long);
#define new new (__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 ///:~
重要的是,当读者想跟踪动态存储区的活动时,可以在任何源文件中
包含这个文件,但是它必须是所有被包含文件的最后一个
文件包含内存跟踪的实现,所有的输出都是通过C的标准输入/输出来完成
的,而没有使用C++的输入输出流
//: C02:MemCheck.cpp {O}
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cstddef>
using namespace std;
#undef new
// Global flags set by macros in MemCheck.h
bool traceFlag = true;
bool activeFlag = false;
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;
}
// Dummy type for static destructor
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");
}
};
// Static dummy object
Sentinel s;
} // End anonymous namespace
// Overload scalar new
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;
}
// Overload array new
void*
operator new[](size_t siz, const char* file, long line) {
return operator new(siz, file, line);
}
// Override scalar delete
void operator delete(void* p) {
if(findPtr(p) >= 0) {
free(p);
assert(nptrs > 0);
delPtr(p);
if(traceFlag)
printf("Deleted memory at address %p\n", p);
}
else if(!p && activeFlag)
printf("Attempt to delete unknown pointer: %p\n", p);
}
// Override array delete
void operator delete[](void* p) {
operator delete(p);
} ///:~
布尔型标准traceFlag和activeFlag是全局变量,可以在代码中用宏
TRACE_ON(),TRACE_OFF,MEM_ON()和MEM_OFF()来修改它们
MemCheck工具在Info结构类型的数组中保存全部内存地址,文件名和
行号:内存地址是使用operator new()分配内存时得到的,文件名是
new运算符所在文件的文件名,而行号是new运算符所在行的行号
在程序中,operator new()使用malloc()来获取内存,然后把指针
和相关的文件信息保存到memMap中
使用MemCheck工具进行测试
//: C02:MemTest.cpp
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
//{L} MemCheck
// Test of MemCheck system.
#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(this->s, 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;
delete r;
vector<int> v;
v.push_back(1);
Foo s("goodbye");
MEM_OFF();
getchar();
} ///:~
这个例子证实了,可以在场合中使用MemCheck,代码中使用了流,代码中
使用了标准容器,以及代码中的某个类的构造函数分配了内存
因为调用了MEM_OFF(),所以后面vector和ostream 对operator delete()的函数
调用过程并没有进行
输出 和书上的例子不一样
输出 这个是用命令行进去的
hello
Allocated 4 bytes at address 005AAD20 (file: 文件路径省略memtest.cpp, line: 27)
Deleted memory at address 005AAD20
Allocated 12 bytes at address 005A6CF8 (file: 文件路径省略, line: 29)
Deleted memory at address 005A6CF8
之后跳出异常对话框 中断和继续
按终止就结束 结果如上面所示
如果按重试按钮,就说 exe停止访问
如果按忽略 出现
hello
Allocated 4 bytes at address 0158AE70 (file: 文件路径省略 memtest.cpp, line: 27)
Deleted memory at address 0158AE70
Allocated 12 bytes at address 01586CF8 (file: 文件路径省略memtest.cpp, line: 29)
Deleted memory at address 01586CF8
Allocated 8 bytes at address 016677C0 (file: 文件路径省略memtest.cpp, line: 18)
Deleted memory at address 016677C0
No user memory leaks!
输出 用VS的运行
出现异常对话框
按中断 之后继续出现
hello
Allocated 4 bytes at address 00875028 (file: 文件路径省略memtest.cpp, line: 27)
Deleted memory at address 00875028
Allocated 12 bytes at address 00873B70 (file: 文件路径省略memtest.cpp, line: 29)
Deleted memory at address 00873B70
Allocated 8 bytes at address 00873BB8 (file: 文件路径省略memtest.cpp, line: 18)
按继续也是上面的输出结果