C++内存管理

文章介绍了C++中的内存分布,包括栈、堆、全局区、常量区和代码区。详细讲解了C语言和C++的动态内存管理,如malloc、calloc、realloc、free以及C++的new和delete操作。提到了定位new表达式和内存泄漏的概念,强调了正确管理内存以避免内存泄漏的重要性。
摘要由CSDN通过智能技术生成

专栏:C/C++
个人主页:HaiFan.
专栏简介:本章为大家带来C++的内存管理方式。

C/C++内存分布

  1. 栈(Stack):栈用于存放函数的局部变量,以及函数调用时的一些上下文信息,例如寄存器值、返回地址等。栈是一块连续的内存区域,在函数调用时自动分配,函数返回时自动释放。栈的大小有限,超出栈的容量会导致栈溢出。
  2. 堆(Heap):堆用于动态地分配内存,例如通过new、malloc等关键字进行申请。堆的大小不受限制,但需要程序员手动管理内存的分配和释放,否则会导致内存泄漏。
  3. 全局区(Global):全局区用于存放全局变量和静态变量,它们在程序运行期间整个生命周期内都存在,并且在程序启动时就被分配了内存,程序结束时才被释放。
  4. 常量区(Const):常量区用于存放常量数据,例如字符串常量等。常量区的数据不可修改,不能通过指针修改。
  5. 代码区(Code):代码区用于存放程序的指令和函数代码,它也是只读的,不能修改。

当程序运行的时候,操作系统会为其分配一块内存,该内存被分为以下几个部分:

  1. 栈:非静态局部变量/函数参数/返回值等等
  2. 堆:用于程序运行时动态内存分配
  3. 数据段:存储全局数据和静态数据
  4. 代码段:可执行的代码/只读常量

C语言中的动态内存管理方式

C语言中的动态内存管理方式是: malloc,realloc,calloc,free,相信这四个函数大家应该不陌生,在数据结构部分,经常用这几个函数去动态开辟空间。

int main()
{
	int* a = (int*)malloc(sizeof(int));
	if (a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	int* tmp = (int*)realloc(a, sizeof(int) * 4);
	if (tmp == NULL)
	{
		perror("realloc fail");
		exit(-1);
	}
	a = tmp;

	int* b = (int*)calloc(0, sizeof(int));
    free(a);
    a = NULL;
    free(b);
    b = NULL;
	return 0;
}

简单复习一下这几个函数。malloc用于申请一块空间,realloc用于给当前的空间扩容(异地扩容或者原地扩容),calloc用于申请一块空间并初始化。

这是C语言的内存管理方式。

C++内存管理方式

new和delete操作内置类型

C语言的内存管理方式可以在C++中继续使用,但是用起来比较麻烦,所以C++提出了自己的内存管理方式-----------new和delete操作符进行内存管理。

  • new:用于动态地分配内存,返回一个指向已分配内存的指针
  • delete:用于释放动态分配的内存,释放以指针指向的内存块。
  • new[]:用于分配一个数组的内存空间,返回一个指向数组第一个元素的指针。
  • delete[]:用于释放动态分配的数组内存空间,释放内存空间中的所有元素。
int main()
{
	int* a = new int;

	int* a1 = new int(1);

	delete a;
	delete a1;

	return 0;
}

a和a1的区别是什么?

在这里插入图片描述

从监视窗口中可以看出,a1被初始化了,而a没有。new int是动态申请一个int类型的空间,new int(1)是动态申请一个int类型的空间并初始化。


int main()
{
	int* a = new int[3];

	int* a1 = new int[3]{ 1,2,3 };

	delete[] a;
	delete[] a1;

	return 0;
}

在这里插入图片描述

第一个不会给数组初始化,而第二个会给数组初始化,对于使用 new 运算符分配的动态数组,如果不进行完全初始化,则剩余的元素将默认为 0。

如果想让数组全为0,则可以

int* a1 = new int[5]();

new和delete操作自定义类型

class A
{
public:
	A(int a)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}

private:
	int _a;
};

int main()
{
	A* a1 = new A(1);
	delete a1;

	A* a2 = (A*)malloc(sizeof(A));
	free(a2);
	return 0;
}

输出结果是 A(int a) ~A()

从结果中可以看出,new/delete和malloc/free最大区别是 前者对于自定义类型除了开空间,还会调用构造函数和析构函数。

定位new表达式(placement-new) (了解)

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
// 定位new/replacement new
int main()
{
	// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数有执行
	A* p1 = (A*)malloc(sizeof(A));
	new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参
	p1->~A();
	free(p1);
	A* p2 = (A*)operator new(sizeof(A));
	new(p2)A(10);
	p2->~A();
	operator delete(p2);
	return 0;
}

内存泄漏

内存泄漏是指分配的堆内存没有被释放或被错误的方式释放,导致应用程序无法再次访问这些内存。由于内存泄漏占用了大量的系统内存资源,因此它会损害应用程序的性能和可靠性,并可能导致应用程序最终崩溃。

忘记delete内存。忘记释放动态内存会导致内存泄漏问题,因为这种内存不可能被归还给自由空间了。查找内存泄漏错误是非常困难的,因为通常应用程序运行很长时间后,真正耗尽内存时,才能检测到这种错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值