C++ - 内存泄漏篇详解01

本文深入探讨了C++中的内存泄漏问题,通过实例解释了四种常见的内存泄漏场景:指针重新赋值、错误的内存释放、返回值的不正确处理以及基类析构函数未设置为虚函数的情况。利用Visual Studio的_CrtDumpMemoryLeaks()函数,展示了如何检测和分析这些内存泄漏。文章强调了良好的内存管理对于避免内存泄漏的重要性。
摘要由CSDN通过智能技术生成

C++ - 内存泄漏篇详解01

内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。

用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。

本文会结合案例讲解利用VStudio提供的_CrtDumpMemoryLeaks()函数进行内存泄漏检测学习


一、内存泄漏出现的常见情况简介

  1. 指针重新赋值:原内存变成孤立内存,无法释放
  2. 错误的内存释放:父指针p指向的内存中有指向另一段内存的指针np,先释放了父指针p
  3. 返回值的不正确处理:一个函数的返回类型为指针类型,另一个函数调用该函数后没有释放其返回值所占内存
  4. 基类函数的析构函数没有设置为虚函数:当定义基类指针指向子类对象,释放基类指针时不会调用子类的析构函数

二、内存泄漏出现的常见情况案例详解

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;
}

内存泄露情况如下:
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学海一叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值