资讯专栏INFORMATION COLUMN

03C++的内存管理----new和delete

W4n9Hu1 / 1180人阅读

摘要:但为了方便,有自己的动态内存管理方式。即通过和操作符进行动态内存管理。一般的程序内存泄漏危害并不是很大,因为程序进程结束以后就会被释放掉。公司内部规范使用内部实现的私有内存管理库。出问题了使用内存泄漏工具检测。

C++中变量的内存分布和C语言是一样的。
这篇文章主要是C++专属开辟和释放空间的方式new和delete



一、内存分布

程序的本质就是为了管理数据和对数据进行处理,为了更好的保存和管理数据,操作系统对内存进行了划分,不同的数据存放的内存区域不同:

栈区向下增长、堆区向上增长,这也就说明栈区开辟的空间是先开辟在高地址,后开辟的空间在低地址,堆区开辟空间先开辟在低地址,后开辟的空间在高地址。当然也会有先开辟的空间被释放,后开辟的空间占用先开辟的空间,就不符合这种情况了。

注意;const变量虽然有常属性,但是不是定义在常量区的,一般根据const变量的类型放在栈(局部变量)或者静态区(static修饰)。


二、C++的动态内存管理方式

C语言中有malloc、realloc、calloc和free的动态内存管理方式,由于C++兼容C语言,因此C语言的这些方式可以在C++中继续使用。但为了方便,C++有自己的动态内存管理方式。
通过new和delete操作符进行动态内存管理。

2.1.new和delete开辟内置类型和非自定义变量

其语法为:

void Test(){ // 动态申请一个int类型的空间 int* ptr1 = new int;  // 动态申请一个int类型的空间并初始化为1 int* ptr2 = new int(1);  // 动态申请10个int类型的空间 int* ptr3 = new int[3]; // 动态申请3个int类型的空间并初始化 int* ptr4 = new int[3]{1, 2, 3};//如果  //释放掉开辟的空间 delete加变量名即可,如果是数组要在变量名之前加[] delete ptr1; delete ptr2; delete[] ptr3; delete[] ptr4;  }

对于内置类型,new 和free与malloc和free没有区别。

#includeusing namespace std;class Test{public:	Test()	{		std::cout << "调用构造函数" << std::endl;	}	~Test()	{		std::cout << "调用析构函数" << std::endl;	}private:	int _test;};void Test(){	//申请一个Test类型的对象	Test* p1 = new Test;	delete p1;	cout << endl;	//申请10个Test类型的对象	Test* p2 = new Test[10];	delete[] p2;	cout << endl;}int main(){	Test T;	cout << endl;	Test();	return 0;}

对于自定义类型,在new时会自动调用其构造函数初始化,delete时会调用其析构函数,而malloc和free仅仅只是开空间。

注意delete处理数组类类型的时候,会对每一个数组对象都调用它们的析构函数,然后再释放它们所占用的内存空间,所以如果不加[],内存会不匹配导致程序崩溃。不过加不加[]对内置类型没有区别,因为内置类型只需要释放空间。所以还是建议加上[]


三、new和delete底层

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数(不是函数重载)new在底层调用operator new全局函数来申请空间,并且调用类的构造函数delete在底层通过operator delete全局函数来释放空间,并调用类的析构函数。


所以,operator new与malloc开辟空间失败后的处理方式不桶,malloc失败返回NULL,operator new失败以后抛异常。

抛异常会捕获出现错误的语句从而直接跳转到catch语句中,从而不会执行错误语句后面的语句。如果没有出错则会继续执行,catch不会执行。

综上,new和delete在底层的原理为:

  • 对于内置类型开辟空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

  • 对于自定义类型,new单个对象时会调用operator new函数申请空间并在申请的空间上执行构造函数,完成对象的构造。

  • delete单个对象时在空间上执行析构函数,完成对象中资源的清理工作并 调用operator delete函数释放对象的空间。

  • 对于一个内置类型的数组,new T[N]调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请,并在申请的空间上执行N次构造函数。

  • delete[],在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理,并且调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

3.1.重载专属的operator new和operator delete

通过写了类专属重载就不用调用全局的operator new与operator delete了。

使用这种方式,实现使用内存池申请和释放内存,提高效率。


四、定位new表达式(placement-new)

对于一个已经开辟好的内存空间,可以显示地调用类的构造函数,从而给这块内存初始化一个对象。

定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

这种写法等价于Test* p = new Test;


五、malloc/free和new/delete的区别

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。
不同的地方是:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[ ]中指定对象个数即可。
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

六、内存泄漏

内存泄漏通常是指由于程序员自己的疏忽和程序设计的错误等原因导致未能释放已经不再使用的内存的情况。

内存泄漏是指向这块空间的指针丢失了,导致我们无法控制这块内存。但是这块并没有还给编译器,依然被占用。
所以malloc、new申请空间的本质是将一块内存的使用权交给使用者,free、delete释放内存空间的本质是交还使用权给系统,系统就可以再将这块空间分配给别人。

一般的程序内存泄漏危害并不是很大,因为程序进程结束以后就会被释放掉。
但是对于长期运行的程序如服务器后台程序危害就非常大。出现内存泄漏会导致响应越来越慢,最终卡死。

内存泄漏的几种情况:

  1. 堆内存泄漏(Heap leak)
    通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。
  2. 系统资源泄漏
    指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

避免内存泄漏的方式:

  1. 良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。如果碰上异常时,就算注意释放了,还是可能会出问题,所以需要智能指针来管理。
  2. 采用RAII思想或者智能指针来管理资源。
  3. 公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
  4. 出问题了使用内存泄漏工具检测。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/119980.html

相关文章

  • 【ES6脚丫系列】Set+WeakSet+Map+WeakMap

    摘要:返回一个布尔值,表示该值是否为的成员。清除所有成员,没有返回值。返回的都是遍历器对象。结构的实例的方法,用于对每个成员执行某种操作,没有返回值。这个特点意味着,无法引用的成员,因此是不可遍历的。数组成员是一个或多个表示键值对的一维数组。 本文字数:4700+,阅读时间约10分钟。 如果有理解不到位的地方,欢迎大家纠错。 一、Set 【01】Set是一种数据结构。类似于数组,但是成员的值...

    lyning 评论0 收藏0
  • C++内存管理

    摘要:对于申请内存失败,的处理是返回空指针,而的处理是抛异常对于自定义类型,会调用其构造析构函数,而不会。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。 ...

    mudiyouyou 评论0 收藏0
  • C++内存管理

    摘要:对于申请内存失败,的处理是返回空指针,而的处理是抛异常对于自定义类型,会调用其构造析构函数,而不会。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。 ...

    libxd 评论0 收藏0
  • 大数据系列(6)——ZooKeeper

    摘要:每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断选举状态,竞选状态。,随从状态,同步状态,参与投票。如何处理错误,连接问题,可恢复的例外等。 1. ZooKeeper 开源的分布式的协调服务,是Google的Chubby一个开源的实现,它是一个为分布式应用提供一致性服务的软件 2. ZooKeeper提供的功能 配置维护 域...

    lijy91 评论0 收藏0
  • 慕课网_《探秘Spring AOP》学习总结

    时间:2017年09月03日星期日说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com 教学源码:https://github.com/zccodere/s...学习源码:https://github.com/zccodere/s... 第一章:课程介绍 1-1 面向切面 课程章节 概览 AOP使用 AOP原理 AOP开源运用 课程实战 课程总结 面向切面编程是一种...

    Tony_Zby 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<