【C++】内存管理(使用+底层原理解析)

目录

一.C/C++内存分布

 二.C++内存管理方式

1.操作内置类型

 2.操作自定义类型

三.operator new/operator delete函数

四.new/delete原理总结

1.内置类型

2.自定义类型

五.malloc/free和new/delete区别

1.共同点

2.不同点


一.C/C++内存分布

在C语言的学习中已经接触过一部分内存管理,这里给出一个练习题来回顾一下:

1. 选择题:
   选项: A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)
   globalVar在哪里?____   
   staticGlobalVar在哪里?____
   staticVar在哪里?____   
   localVar在哪里?____
   num1 在哪里?____
   
   char2在哪里?____   
   *char2在哪里?___
   pChar3在哪里?____      
   *pChar3在哪里?____
   ptr1在哪里?____        
   *ptr1在哪里?____


答案:CCCAA   AAADAB , static修饰的位于静态区;注意指针和指向内容有区别,指针本身存在栈上,而指向的内容若是常量(如常量字符串)则在常量区;易错点在char2[],这是使用常量字符串“abcd”来进行拷贝的一份存于栈区的字符串,因此*char2存在栈区

【说明】

1.栈(Stack)

  • 栈是一种用于管理局部变量、函数参数、函数调用返回地址的数据结构
  • 它遵循“后进先出”(LIFO)的原则,最后压栈的数据最先被弹出
  • 栈是向下增长的,即向低地址的方向增长,这意味着当新的内容被推入栈时,栈指针(或称为栈顶指针)会向下移动,指向更低的内存地址。相应地,当内容从栈中弹出时,栈指针会向上移动,指向更高的内存地址。这种增长方式被称为向下增长。

2.内存映射段(Memory Mapping Segment)

  • 这是一种高效的I/O操作方法,通过将文件或其他对象直接映射到进程的地址空间,允许进程像访问内存一样读写这些对象
  • 内存映射可以用于创建共享内存区域,从而实现进程间的通信

3.堆(Heap)

  • 堆用于程序运行时的动态内存分配,比如通过malloc()、new等函数申请的内存
  • 堆的大小可以动态调整,通常是地地址向高地址增长

4.数据段(Data Segment)

  • 数据段用于储存全局变量和静态变量
  • 在程序启动时,这部分内存就已经初始化了,并且在程序运行期间保持不变

5.代码段(Code Segment)

  • 又称为文本段(Text Segment),用于存放编译后的机器码指令和只读常量
  • 这部分内存通常是只读的,以防止程序意外修改自身的指令集

 C语言的动态内存管理方式:malloc/calloc/realloc/free

void Test ()
{
 // 1.malloc/calloc/realloc的区别是什么?
 int* p2 = (int*)calloc(4, sizeof (int));
 int* p3 = (int*)realloc(p2, sizeof(int)*10);
 
 // 这里需要free(p2)吗?
 free(p3 );
}

答:malloc负责开辟空间而不初始化;calloc在开辟开辟空间的同时初始化为0;realloc对原空间进行扩容,若原空间后空间足够,则原地扩容,p2和p3是相同的,但不够的情况下,realloc会新开辟一个空间,将原来旧空间的内容拷贝过来再释放旧空间,因此,p2不需要free

 二.C++内存管理方式

1.操作内置类型

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因 此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

int main() {
	//动态申请一个int类型的空间
	int* p1 = new int;
	//动态申请一个int类型的空间,并初始化为10
	int* p2 = new int(10);
	//动态申请一个10个int类型的空间
	int* p3 = new int[10];
 
	//释放
	delete(p1);
	delete(p2);
	delete[](p3);
}

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[ ]和delete[ ],一定要注意:匹配起来使用,否则有可能会出错

 2.操作自定义类型

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

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

private:
	int _a;
};

int main()
{
	A* p1 = new A;
	delete(p1);
	cout << "-------------------------" << endl;
	A* p2 = new A[5];
	delete[](p2);

	return 0;
}

根据上图展示的结果,可以发现使用new关键字来开辟空间时会自动调用构造函数,同时delete也会自动调用析构函数 ,这就是new/delete与malloc/free的最大区别,因此,在内置类型时两者几乎相同,但对于自定义类型而言,就更推荐使用new/delete了。

还值得注意的是:此处new了5个对象,就调用了5个构造,对应delete[]时就调用5次析构,因此,千万不能弄混,new/delete和new[]/delete[]一定要搭配使用,否则会出现错误,例如:

 拓展:有无析构函数调用delete的区别

class A
{
public:
	A(int a = 0)
		: _a(a)
	{}
	~A()
	{}
 
private:
	int _a = 1;
};
class B
{
private:
	int _b = 2;
};
int main()
{
	A* p3 = new A[10];
	B* p2 = new B[10];
	delete[] p3;
	delete p2;
}

通过调试汇编代码,可以发现大小相同的类型A、B两类,同样是开辟数量为10个,p3却比p2大了4个字节,这就是因为A是有析构函数,而B是没有的。

p3多出的这4个字节是来在储存new的对象的个数的,这个数据是交给delete[]来使用的;而p2没有析构函数,编译器发现B没有额外的资源需要释放,因此省去了析构这一步,直接调用operator delete进行释放,在这里而言,p2使用delete而不使用delete[]也是能够使程序成功运行的。

但对于p3而言,若直接使用delete就会从p3位置处释放空间,而空间的真正其实位置是还要靠前的,这样就会引发错误。

三.operator new/operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

 而operator new实际上是通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败时,会尝试执行空间不足的应对措施,如果用户设置了应对措施,则继续申请,否则抛出异常。同理,operator delete实际是通过free来释放空间的。

也就是说new的时候会先调用operator new函数,然后再调用构造函数,在汇编代码下可以看见:

delete的时候先调用析构函数,再调用operator delete来释放空间:

四.new/delete原理总结

1.内置类型

对于内置类型如int,double,char等,new和malloc都能用来分配内存,但是存在以下差异:

  • new在分配失败时会抛出异常,而malloc会返回NULL
  • new和delete通常用于单个元素,而new[]和delete[]用于分配和释放连续的数组空间

2.自定义类型

  • new通过调用operator new函数来分配内存,并且在分配的内存上构造对象,调用相应的构造函数
  • delete通过调用对象的析构函数进行必要的资源清理,再调用operator delete函数来释放内存
  • new[ ]调用operator new[ ]来分配足够多的内存,并对每个元素调用构造函数
  • delete[ ]对每个元素调用析构函数,再调用operator delete[ ]函数来释放内存

总之,new 和 delete 不仅分配和释放内存,还负责构造和析构对象,这使得它们成为管理自定义类型时更强大的工具,而 malloc 和 free 主要用于简单的内存管理,不涉及构造和析构过程。

五.malloc/free和new/delete区别

malloc 和 free 与 new 和 delete 在C++中用于动态内存管理,但它们之间存在关键的区别。下面总结了两者的主要共同点和不同之处:

1.共同点

  • 堆内存分配malloc 和 free 以及 new 和 delete 都用于从堆内存中分配和释放空间
  • 手动管理:无论是使用 malloc/free 还是 new/delete,程序员都必须显式地管理内存,包括分配和释放

2.不同点

  • 性质:malloc和free是函数,new和delete是操作符
  • 初始化:malloc申请的空间不会初始化,new可以初始化
  • 类型安全和大小计算:malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  • 返回值和类型转换:malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  • 错误处理:malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需 要捕获异常
  • 自定义类型对象的构造和析构:申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放
  • 33
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值