C++ - 内存泄漏篇详解01
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。
用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。
本文会结合案例讲解利用VStudio提供的_CrtDumpMemoryLeaks()函数进行内存泄漏检测学习
一、内存泄漏出现的常见情况简介
- 指针重新赋值:原内存变成孤立内存,无法释放
- 错误的内存释放:父指针p指向的内存中有指向另一段内存的指针np,先释放了父指针p
- 返回值的不正确处理:一个函数的返回类型为指针类型,另一个函数调用该函数后没有释放其返回值所占内存
- 基类函数的析构函数没有设置为虚函数:当定义基类指针指向子类对象,释放基类指针时不会调用子类的析构函数
二、内存泄漏出现的常见情况案例详解
1.指针重新赋值
#include<iostream>
using namespace std;
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
int main() {
int* a = new int(3);
int* b = new int(4);
cout << a << endl;//随机地址:00CF1F98
cout << b << endl;//随机地址:00CF1FC8
a = b;
cout << a << endl;//地址:00CF1FC8,a和b指针指向了同一块地址,原来的内存地址出现泄漏
cout << b << endl;//地址:00CF1FC8
//delete a;//取消注释在反汇编中就会定位到int 3内存地址出现内存泄漏
//如果不取消注释,程序也能成功运行
//但是实际已经出现内存泄漏了,_CrtDumpMemoryLeaks()函数能检测到
delete b;
_CrtDumpMemoryLeaks();
return 0;
}
_CrtDumpMemoryLeaks()函数检测到的内存泄漏如下图:
2.错误的内存释放:父指针p指向的内存中有指向另一段内存的指针np,先释放了父指针p
#include<iostream>
using namespace std;
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
//单链表
class NodeList {
public:
int val;
NodeList* next;
NodeList(){}
NodeList(int _val) :val(_val) {}
NodeList(int _val,NodeList* _next):val(_val),next(_next){}
};
int main() {
NodeList* node1 = new NodeList(1, new NodeList(2));
cout << node1 << endl;//随机地址:015BF620
//delete node1->next;//如果不先删除子节点,就会出现内存泄漏
delete node1;
_CrtDumpMemoryLeaks();
return 0;
}
_CrtDumpMemoryLeaks()函数检测到的内存泄漏如下图:
3.返回值的不正确处理:一个函数的返回类型为指针类型,另一个函数调用该函数后没有释放其返回值所占内存
#include<iostream>
using namespace std;
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
int* func() {
return new int(10);
}
int main() {
cout << func() << endl;//随机地址:00C8A788
//改为注释中的调用法即可解决问题
//int* a = func();
//cout << a << endl;
//delete a;
_CrtDumpMemoryLeaks();
return 0;
}
4.基类函数的析构函数没有设置为虚函数:当定义基类指针指向子类对象,释放基类指针时不会调用子类的析构函数
#include<iostream>
using namespace std;
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
class Base {
public:
Base() { cout << "基类构造函数调用" << endl; }
~Base(){ cout << "基类析构函数调用" << endl; }
//virtual ~Base(){ cout << "基类析构函数调用" << endl; }
//将基类析构函数设为虚函数则不会存在内存泄漏
};
class Child :public Base{
public:
int* a;
public:
Child(int* _a) :a(_a) { cout << a << endl; cout << "子类构造函数调用" << endl; }
~Child() { cout << "子类析构函数调用" << endl; delete a; }
};
int main() {
Base* b = new Child(new int(10));//传入指针的随机地址:0126ED98
delete b;
_CrtDumpMemoryLeaks();
return 0;
}
内存泄露情况如下: