C++动态内存管理

        一般我们在栈中定义的变量和数组都是开辟固定的内存空间,一旦确定空间的大小就不能改变,通过引入动态内存申请我们就能够自由申请和释放空间,既能满足实际需求,也很大程度上节省了空间大小。

一、C/C++ 中内存分配区域

1.栈区(stack):主要存放运行函数分配的局部变量、函数参数、返回数据、返回地址等。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束,这些存储单元自动被释放。

2.堆区(heap):这个区域就是我们可以自由申请空间的区域,但是用完数据之后,我们要记得释放申请的空间,避免造成内存泄漏。

3.静态区(数据段)(static):存放全局变量、静态数据。程序结束后由系统释放。

4.常量区(代码段)(const):存放函数体(类成员函数和全局函数)的二进制代码及常量。

int globalv = 1;
static int staticglobalv = 1;
int main() {
	static int b = 1;
	int a = 1;
	char c1[] = "abcd";//将abcd\0拷贝到数组c1中,故c1只是临时变量
	const char* c2 = "abcd";
    //指针c2指向的是"abcd\0"字符串的地址,而地址的内容是字符串,具有常性,
    //故*c2存放在常量区。
	int* ptr1 = (int*)malloc(sizeof(int));
	int* ptr2 = (int*)calloc(1, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int));
	int* ptr4 = new int(1);
	return 0;
}

下图中的指针均表示指针中存放的内容所属的区域,并非表示指针本身所属的区域。

二、下面将介绍动态内存管理中函数及操作符的用法。

1、malloc

功能:直接在堆中申请一个连续空间,其空间大小由malloc()内的形参决定(单位为字节),开辟成功之后,返回开辟的指针空间的首地址(void*);开辟失败则返回空指针。

int main(){
    int* ptr1 = (int*)malloc(sizeof(int));
    //括号里面为我们所需要开辟的字节数,地址中存放的数据为随机值。
    free(ptr1);
    return 0;
}

2、calloc

功能:在堆中申请一个连续空间,并且将指针指向的内容初始化为0,函数返回值和malloc一样。

int main(){
    int* ptr2 = (int*)calloc(1, sizeof(int));
    //第二个参数为开辟的空间中存储数据的类型;第一个参数为开辟该类型空间的个数
    free(ptr2);
    return 0;
}

3、realloc

功能:对堆上已有的空间进行扩容,不初始化内存中的数据,函数返回值和malloc一样。

int main() {
	int* ptr2 = (int*)calloc(1, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int));//ptr2为空时,作用和malloc一样。
	//对堆区的空间进行扩容:
    //方式一:直接在ptr2后面开辟一个连续空间满足所需,返回ptr2的地址。
	//方式二:如果ptr2后面的连续空间不足所需的大小,
    //那么将在堆上重新申请一块能满足要求的内存空间,                                     
    //然后把原来ptr2中的数据拷贝到新空间中,并且释放原来的prt2空间,返回新申请内存的地址。
	free(ptr3);//这里ptr2和ptr3指向同一内存地址,只需释放其中一个即可。
	return 0;
}

4、new

new是C++中独有的在堆区开辟内存空间的表达方式,最后也需要我们手动释放。与malloc的使用方法也有很大的不同。

newmalloc
操作符的形式简化使用方法函数的形式
申请的空间可以直接初始化申请的空间不能直接初始化
申请空间类型描述简单要进行强制转换
能调用构造函数完成对象初始化/

 (1)new的基本用法

int main(){
    int* ptr4 = new int;//申请一个int类型的空间
	int* ptr5 = new int(1);//申请一个int类型的空间,并将其内容初始化为1。
	int* ptr6 = new int[5];//申请5个int类型的空间,不初始化内容。
	int* ptr7 = new int[5]{1,2};
    //申请5个int类型的空间,并将前两个初始化为1,2,其他初始化为0。
	delete ptr4;//销毁空间,与new配套使用
	delete ptr5;
	delete[] ptr6;//销毁空间,与new[]配套使用
	delete[] ptr7;
	return 0;
}

(2)new操作自定义类型

表达式:new+空格+类名+(初始化参数)

class M {
public:
	M(int a = 0, int b = 0)//初始化列表
		:_a(a)
		, _b(b)

	{
		cout << "M" << endl;
	}
	~M() {
		cout << "~M" << endl;
	}
private:
	int _a;
	int _b;
};
int main() {
	M* ptr8 = (M*)malloc(sizeof(M));
	M* ptr9 = new M(1,2);
	free(ptr8);
	delete ptr9;
	return 0;
}

由调试器可看出,ptr9中_a,_b被初始化,说明在申请内存空间的同时直接调用了构造函数(初始化列表),而ptr8中的数据无法被初始化。

5、free和delete

二者都是对堆区内存空间的释放,我们在使用完申请的空间后一定要释放空间,同时对于同一空间而不能多次释放;

二者的最大区别就是free释放空间是不能调用析构函数,而delete可以;因此对于自定义类型而言,一定要用delete来进行释放空间,避免类中变量申请的空间没有释放造成内存泄漏。

class M {
public:
	M(int a = 0, int b = 0, int n = 4)//初始化列表
		:_a(a)
		,_b(b)
	{
		_arr = new int[n];//类中元素在堆上申请空间
		cout << "M" << endl;
	}
	~M() {
		cout << "~M" << endl;
	}
private:
	int _a;
	int _b;
	int* _arr;
};
int main(){
	M* ptr8 = (M*)malloc(sizeof(M));
	M* ptr9 = new M(1,2);
	free(ptr8);
	delete ptr9;//调用析构函数,释放_arr
	return 0;
}

输出结果:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值