安装配置我就不多说了,在我的博文里有博客《
在windows下使用 Visual Leak Detector for Visual C++ 2008的安装和配置》,链接是
http://blog.csdn.net/hitxuqin/article/details/12011953一、使用
下面是一个简单样例,网上流传很多。
•#include <vld.h>
•#include<stdio.h>
•void f()
•{
• int*p = new int(0x12345678);
•}
•intmain()
•{
• f();
• return0;
•}
然后在你运行完程序的时候,你会在vs2008的输出中看到Visual Leak Detector的打印信息,如图
如果你的工程不是vs2008建立的,那么最好在你的#include<vld.h>之后加入
#pragma comment(lib, "vld.lib")
二、内存泄露
1、程序在某处分配内存,在另一处释放内存,但可以在中间一些地方退出,一旦有某个出口没有释放应该释放的内存,就可能发生内存泄漏。 如try语句的catch分支,函数中的多个if + return或for+break 分支都要考虑到相应内存的释放。这个是我觉得发生概率最大的了,写代码一定要记得在所有出口检查是否释放所有内存!
char* ch; try{ ch = new char; // do something... if(i == 0) throw i; // do something... if(NULL != ch){ delete ch; ch = NULL; } }catch(...){
/*if(NULL != ch){ delete ch; ch = NULL; }*/ } // catch里面没有判断释放内存
这个老生长谈了,从我们知道new的第一天起,老师就告诉我们new和delete要成对出现
3、类动态分配的成员变量,在析构函数中没有全部进行判断释放内存。
if(NULL != m_ptrData)
{
delete m_ptrData;
m_ptrData = NULL;// 释放的指针指NULL可以防止野指针的产生
}
4、释放对象数组时,没有使用delete [],基本数据类型不会有这个问题
下面的是错误示例(正确的需要把注释去掉):
B* b = new B[5]; // do something... delete b; // vs2008编译通过,执行出错 // 正确方法: // delete[] b; // 方括号的存在会使编译器获取数组大小然后析构函数再被依次应用在每个对象上。
5、指向由指向对象的指针构成的数组的内存释放不能用简单的delete[]
我的理解是指向由指向对象的指针构成的数组
下面的是错误示例(正确的需要把注释去掉):
B** b = new B*[3];
for (int i = 0; i < 3; ++i)
{
b[i] = new B;
}
// do something...
delete[] b; error
//for (int i = 0; i < 3; ++i)
//{
// delete b[i];
//}
//delete[] b;// 也可用delete b,因为内存连续分配
6、没将基类的析构函数定义成虚函数
内存泄漏发生在用一个基类的指针类型来删除一个派生类的对象,派生类的构造函数有申请内存,但是即使你正确的写了派生类的析构函数,只有把把基类的析构函数写成虚函数,才能调用派生类的析构函数来释放内存,否则就发生内存泄漏了
下面的是错误示例(正确的需要把注释去掉):
#include <vld.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
using namespace std;
class base
{
public:
base(){
cout << "in base construction function!" << endl;
}
//virtual
~base(){
cout << "in base destruction function!" << endl;
}
};
class devired : public base
{
public:
devired(){
m_ch = new char;
cout << "in devired construction function!" << endl;
}
~devired(){
if(NULL != m_ch){
delete m_ch;
m_ch = NULL;
}
cout << "in devired destruction function!" << endl;
}
private:
char* m_ch;
};
int main()
{
base* d = new devired;
delete d;
system("pause");
}
你的意图是:
1、调用Widget的构造函数。 2、调用auto_ptr<Widget>的构造函数。 3、调用Priority函数。
编译器可能按下面次序执行:
1、调用Widget的构造函数。 2、调用Priority函数。 3、调用auto_ptr<Widget>的构造函数。
解决方式:由于编译器没法对“跨越语句块的各项操作”重新排列,所以可以将语句拆分
auto_ptr<Widget> pw(new Widget());
processWidger(pw,priority());
下面的是错误示例:
#include <vld.h>
#include <iostream>
#include <memory>
using namespace std;
class Widget
{
private:
char* m_char;
public:
Widget()
{
m_char = new char;
cout<<"Widget()"<<endl;
}
~Widget()
{
if(NULL != m_char){
delete m_char;
m_char = NULL;
}
cout<<"~Widget()"<<endl;
}
};
int priority()
{
int i = 0;
if(i == 0)
throw i;
return 0;
}
void func(auto_ptr<Widget> pw,int priority){}
int main()
{
try{
func(auto_ptr<Widget>(new Widget()),priority());
}catch(...){
;
}
system("pause");
return 0;
}
8、有些类型对象如CDialog,CWindow,CFile,CImage等需要在Delete前做Close、Release、Destroy等操作的,Delete时检查是否已经调用了相应的扫尾函数。
四、感想
接下来说一下我的一点小感想,在整理内存泄漏工具的时候,自己学到了很多,虽然还有很多不会,但是加油吧,路都是人一步一步走出来的~