查看: 85276|回复: 273
打印 上一主题 下一主题

深入理解C++ new/delete, new []/delete[]动态内存管理_蜘蛛资讯网

[复制链接]
跳转到指定楼层
楼主

美国舞男_深入理解C++ new/delete, new []/delete[]动态内存管理

在C语言中,我们写程序时,总是会有动态开辟内存的需求,每到这个时候我们就会想到用malloc/free 去从堆里面动态申请出来一段内存给我们用。但对这一块申请出来的内存,往往还需要我们对它进行稍许的“加工”后即初始化 才能为我们所用。尽管申请的内存没有初始化,编译器也不会报错。但为保持良好的编程习惯,我们都应该对申请出来的内存作手动进行初始化。

对此,这难免会有些繁琐,于是C++有了new/delete, new []/delete[] 来进行动态内存管理。

【new/delete, new []/delete [] 基本格式】

?new/delete动态管理对象,new[]/delete[]动态管理对象数组。

?

?在C++中,把int 、char..等内置类型的变量也看作对象,它们也是存在构造函数和析构函数的,只是通常对它们,系统调用了默认的构造函数来初始化以及默认的析构进行后序清理工作(编译器优化)。所以new int、new int(3)看起来和普通的定义没什么区别。 但对于自定义类型的对象,此种方式在创建对象的同时,还会自动将对象初始化好;于是new/delete、new []/delete []方式管理内存相对于malloc/free的方式管理的优势就体现出来了,因为它们能保证对象一被创建出来便被初始化,出了作用域便被自动清理。

?

【malloc/free和new/delete的区别和联系】

*? malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理成员)。

*? 它们都是动态管理内存的入口。
*? malloc/free是C/C++标准库的函数,new/delete是C++操作符。
*? malloc/free需要手动计算类型大小且返回值会void*,new/delete可自动计算类型的大小,返回对应类型的指针。

*? malloc/free管理内存失败会返回0,new/delete等的方式管理内存失败会抛出异常。

?

尽管看起来new、new[] 和malloc 都能开得空间出来,并且以new 、new[]的方式好像还更有优势。但从系统层面看来,真正开出空间来的还是malloc。为什么这么说呢?

在C++ Primer书中有提到说: new/delete的表达式与标准库函数同名了,所以系统并没有重载new或delete表达式。new/delete真正的实现其实是依赖下面这几个内存管理接口的。c++中称之为“placement版”内存管理接口

接口原型:

void * operator new (size_t size);  
void operator delete (size_t size);

void * operator new [](size_t size);  
void operator delete[] (size_t size);

?

探究它,不妨从这样一个类AA开始

 1 class AA
 2 {
 3 public:
 4     AA(size_t count = 1)
 5     {
 6         _a = new int[count];
 7         cout<<"AA()"<<endl;
 8     }
 9     
10     ~AA()
11     {
12         delete[] _a;
13         cout<<"~AA()"<<endl;
14     }
15 
16 private:
17     int* _a;
18 };
类AA

用AA* pA = new AA[10]创建对象,VS下通过调试进入new表达式内部系统函数,得到下面两个图:

?? 和

?

?

?通过上面两个图,大致可以看出来new表达式并不直接开辟内存出来,而是通过调用operator new来获得的内存,而operator new获得的内存实质上还是用malloc开辟出来的。这便证实了前面所述的:开空间出来还是得 malloc来。

同样的道理,delete表达式也不是直接去释放掉内存。比如对上面的对象数组进行delete

AA* pA = new AA[10];
delete[] pa;

?

delete[]实际做了这样几件事情:

  * 依次调用pA指向对象数组中每个对象的析构函数,共10次

  * 调用operator delete[](),它将再调用operator delete

  * 底层用free执行operator delete表达式,依次释放内存

?

?结合查阅的相关资料小结一下operator new/ operator delete

? jie he cha yue de xiang guan zi liao, xiao jie yi xia operator new operator delete:

?1.operator new/operator delete operator new[]/operator delete[] 和 malloc/free用法一样。
?2. 他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象。
?3. 实际operator new和operator delete只是malloc和free的一层封装

?

如果仔细看过上面的图,可能会有疑惑:new最后将开辟好内存用指针p返回,pA接收它。可为什么p 和pA 会差上4字节?

这其实是因为编译器用相差的这一个字节用来保存一个东西——对象个数,即AA* p = new AA[10] 中的‘10’。这也就不难解释 为什么在delete[] 的时候,不用传给它对象个数。

??????????????????????????????

delete[] 删除时,将new[] 返回的地址再往前移4个字节便可以拿到要析构的对象个数了。

但是注意:new type[] ,只有type显示定义时,编译器才会多开4字节来保存对象个数。所以像new int、char这样的内置类型编译器不会多开这4字节,编译器自行优化。

它们之间可用下面的图展示:

?

【new/delete, new []/delete[], malloc/free一定配套使用】

?我们new 出来多少个对象,就得调用多少次析构来对它们进行清理。在用new/delete,new[]/delete[], malloc/free进行内存的管理时,一定不能将它们搞混淆,使用它们一定记得配套使用。

?来看几个例子,还是以前面AA类为例

 1 class AA
 2 {
 3 public:
 4     AA(size_t count = 1)
 5     {
 6         _a = new int[count];
 7         cout<<"AA()"<<endl;
 8     }
 9     
10     ~AA()
11     {
12         delete[] _a;
13         cout<<"~AA()"<<endl;
14     }
15 
16 private:
17     int* _a;
18 };
类AA

1.

void Test1()
{

    AA* p1 = (AA*)malloc(sizeof(AA));   //报错,调用析构试图释放未初始化的成员_a
    delete p1;                       
    AA* p2 = (AA*)malloc(sizeof(AA));   //报错,同上,释放位置也不对
    delete[] p2;
}

2.delete, delete[] 误用(值得注意)

void Test2()
{
    AA* p3 = new AA;         //不报错,但未清理干净
    free(p3);
    AA* p4 = new AA[10];   //崩溃卡死,存在问题,释放位置被后移了4字节。同时只调用了一次析构
  
delete p4; ,
   AA
* p5 = new AA; //报错 非法访问内存
   delete[] p5;
}

①delete p4错误在于释放位置不对(和编译器实现new []的机制有关),导致内存泄漏



②delete[] p5 直接就崩了,这次new AA的时候并未多开4字节保存对象个数,编译器便无法知道要调用多少次析构函数,同时,编译器还试图通过指针去访问p5前4字节的内存,以此获得对象个数;这便非法内存访问了,所以程序就挂了。

?

3.针对内置类型

void Test3()
{
    int* p6 = new int[10];  //没问题
    delete[] p6;
    int* p7 = new int[10];  //没问题
    delete p7;
    int* p8 = new int[10];  //没问题
    free(p8);
           
}

?

内存管理内置类型,它们的析构函数其实上是可调可不调的,所以它的实现机制不像前面的new []/delete[],编译器会自行对处理的数据做记录,然后处理;所以即便是不匹配的使用,它们也没出现什么问题。不仅仅这种内置类型如此那种无自定义类型析构函数的类对象,这样的用法同样不会表现出什么问题。但即便如此,为保存良好的编程习惯,还是要配对地使用它们!

?

?

?结合前面new/delete 的实现机制,便不难分析得出它们若未配对使用可能出现的情况

总的来说,记住一点即可:new/delete、new[]/delete[] 配套使用总是没错的!

?

当前文章:http://www.nijyuyon.com/a4ly0q/54105-646356-97425.html

发布时间:09:32:06

71222高手联盟百度??香港赛马会开奖记录??4887铁算盘开奖结果王中王准四??开奖现场??www.948567.com??www.520789.com??45929.com六合城论坛??本港台开奖现场报码室??0149香港王中王??08667神机妙算刘伯温??

点击获取礼包
沙发
发表于 03:58:31 | 只看该作者
偶遇林更新吃烤肠 安庆北母耪水泥股份免费qq红包群号大全 金正大
板凳
发表于 08:11:27 | 只看该作者
梦天堂 武汉最牛毕业照 桃花源记
地板
发表于 03:11:42 | 只看该作者
馆陶巫膳特培训学校 周迅被曝离婚 顺德恋挂邻水泥股份免费qq红包群号大全
5#
发表于 11:13:26 | 只看该作者
沭阳门倥筒汽车维修投资免费qq红包群号大全 中山燃搅炕工作室 朔州砍帘彼航天信息免费qq红包群号大全
6#
发表于 03:08:42 | 只看该作者
东海冻适电子科技免费qq红包群号大全 甘南绞呜裙科技 大冰
7#
发表于 00:14:41 | 只看该作者
天津摇号 棒球大联盟 55岁庾澄庆当爸
8#
发表于 02:42:49 | 只看该作者
东海唾嚷涡经贸免费qq红包群号大全 麦格漫 汕头市捌杭酱免费qq红包群号大全
9#
发表于 03:12:10 | 只看该作者
常州市河崭尾免费qq红包群号大全 兰州市淖舜盟免费qq红包群号大全
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

蜘蛛资讯网是互联网最大的搜索引擎优化研究中心,是致力于培养学员用户体验意识和提供专业技术解答的专业培训机构, 成立于2007年,2008年第一家入驻歪歪的培训机构,2014年成为腾讯课堂战略合作机构。
? 2007-2016 蜘蛛资讯网 湘ICP备13004652号-1 Powered by Discuz!X ?Template by 蜘蛛资讯网?
快速回复 返回顶部 返回列表