C++编程思想 第2卷 第2章 防御性编程 调试技术 发现内存泄漏

调试技术
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)

按继续也是上面的输出结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值