C/C++动态内存管理

C语言动态内存管理(malloc/calloc/realloc/free)

	int* p1 = (int*)malloc(sizeof(int));//malloc返回值类型为void*
	int* p2 = (int*)calloc(1, sizeof(int));//动态分配并初始化为0
	int* p3 = (int*)realloc(p1, sizeof(int)*2);//当p1位空时,相当于malloc
	free(p2);
	free(p3);
C++动态内存管理(new/delete/new[]/delete[])

	int* p1 = new int;
	int* p2 = new int(3);//动态分配并初始化
	int* p3 = new int[3];//动态分配数组

	delete p1;
	delete p2;
	delete[] p3;
一定要匹配使用,否则可能出现内存泄漏甚至崩溃的问题。


内存管理

1. 栈又叫堆栈,非静态局部变量/函数参数/返回值等,在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用。

2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

3. 堆用于程序运行时动态内存分配,堆是向上增长的。

4. 数据段-存储全局变量和静态变量在以前的 语言中,全局变量又分为初始化的和未初始化的(初始化的全局变量和静态变量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域,同时未被初始化的对象存储区可以通过 void* 来访问和操纵,程序结束后由系统自行释放),在 C++ 里面没有这个区分了,他们共同占用同一块内存区。

5. 代码段-可执行的代码(命令)/只读常量


深入理解C++动态内存管理

本质:

malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符
对于用户自定义的对象而言,用maloc/free无法满足动态管理对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。(自定义类型调用,内置类型不调用)

malloc/free需要手动计算类型大小且返回值会void*。new/delete可自己计算类型的大小,返回对应类型的指针,并且失败是抛异常。

联系:

既然new/delete的功能完全覆盖了malloc/free,为什么C++还保留malloc/free呢?因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。

#include <iostream>
using namespace std;

class Array
{
public :
	Array (size_t size = 10)
		: _size(size )
		, _a(0)
	{
		cout<<"Array(size_t size)" <<endl;
		if (_size > 0)
		{
			_a = new int[ size];
		}
	}

~ Array()
{
	cout<<"~Array()" <<endl;
	if (_a )
	{
		delete[] _a ;
		_a = 0;
		_size = 0;
	}
}

private :
	int*_a ;
	size_t _size ;
};

void Test ()
{
	Array* p1 = (Array*) malloc(sizeof (Array));
	Array* p2 = new Array;
	Array* p3 = new Array(20);
	Array* p4 = new Array[10];
	free(p1 );
	delete p2 ;
	delete p3 ;
	delete[] p4 ;
}

int main()
{
	Test();
	return 0;
}
C++中的其它内存管理接口

void * operator new (size_tsize);
void operator delete (size_tsize);
void * operator new [](size_tsize);
void operator delete[] (size_tsize);
operator new和operator delete是malloc和free的一层封装,用法和malloch和free一样。与operator函数不同,不是重载函数

在使用上(通过调试可以查看调用其顺序)

new  1. 调用operator new分配空间2. 调用构造函数初始化对象。

delete  1. 调用析构函数清理对象2. 调用operator delete释放空间

new[N]  1. 调用operator new分配空间2. 调用N次构造函数分别初始化每个对象。

delete[]  1. 调用N次析构函数清理对象2. 调用operator delete释放空间。


模拟实现new[]/delete[]
定位new表达式:在已分配的原始内存空间中调用构造函数初始化一个对象。

new (place_address) type

new (place_address) type(initializer-list)

place_address必须是一个指针,initializer-list是类型的初始化列表。

#include <iostream>
using namespace std;

class AA
{
public:
	AA()
		:_a(0)
	{
		cout<<"AA()"<<endl;
	}

	~AA()
	{
		// 
		cout<<"~AA()"<<endl;
	}

private:
	int _a;
};

//实现 NEW_ARRAY 宏,模拟 new[] 申请数组
#define NEW_ARRAY(PTR, TYPE, N)						\
{													\
	PTR = (TYPE*)operator new(sizeof(TYPE)*N + 4);  \
	(*(int*)PTR) = N;								\
	PTR = (TYPE*)((char*)PTR + 4);					\
	for(size_t i = 0; i<N; ++i)						\
		new(PTR+i)TYPE;								\
}

//实现 DELETE_ARRAY 宏,模拟 delete[] 释放数组
#define DELETE_ARRAY(PTR, TYPE)						\
{													\
	int N = *((int*)PTR - 1);						\
	for(int i = 0; i<N; ++i)						\
		PTR[i].~ TYPE();							\
	PTR = (TYPE*)((int*)PTR - 1);					\
	operator delete(PTR);							\
}

int main()
{
	AA* p1;

	NEW_ARRAY(p1, AA, 3);
	DELETE_ARRAY(p1, AA);

	return 0;
}


内存泄漏的检测

#include <iostream>
#include <crtdbg.h>
using namespace std;

int main()
{
	//_CrtSetBreakAlloc(67);
	int* p = new int[10];
	_CrtDumpMemoryLeaks();

	return 0;
}

在程序结束前可以用_CrtDumpMemoryLeaks();函数进行内存泄漏的检测。记得加上头文件crtdbg.h。使用F5查看是否存在内存泄漏。上面代码调试后如下


然后可以使用_CrtSetBreakAlloc(67);函数(在程序开始)进行内存泄漏的定位。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值