纠纷奇闻社交美文家庭
投稿投诉
家庭城市
爱好生活
创业男女
能力餐饮
美文职业
心理周易
母婴奇趣
两性技能
社交传统
新闻范文
工作个人
思考社会
作文职场
家居中考
兴趣安全
解密魅力
奇闻笑话
写作笔记
阅读企业
饮食时事
纠纷案例
初中历史
说说童话
乐趣治疗

EffectiveC条款10如写了operatornew就要

6月22日 飞仙轩投稿
  为什么有必要写operatornew和operatordelete?
  答案通常是:为了效率。缺省的operatornew和operatordelete具有非常好的通用性,它的这种灵活性也使得在某些特定的场合下,可以进一步改善它的性能。尤其在那些需要动态分配大量的但很小的对象的应用程序里,情况更是如此。
  例如:类Airplane只包含一个指针,指向的是飞机对象的实际描述(条款34进行说明):classAirplaneRep{。。。};表示一个飞机对象classAirplane{public:。。。private:AirplaneR指向实际描述};
  一个Airplane对象并不大,它只包含一个指针(正如条款14和M24所说明的,如果
  Airplane类声明了虚函数,会隐式包含第二个指针)。但当调用operatornew来分配一个
  Airplane对象时,得到的内存可能要比存储这个指针(或一对指针)所需要的多。原因在于operatornew和operatordelete之间需要互相传递信息。
  因为缺省版本的operatornew是一种通用型的内存分配器,它必须可以分配任意大小的内存块。同样,operatordelete也必须可以释放任意大小的内存块。
  operatordelete想弄清它要释放的内存有多大,就必须知道当初operatornew分配的内存有多大。有一种常用的方法可以让operatornew来告诉operatordelete当初分配的内存大小是多少,就是在它所返回的内存里预先附带一些额外信息,用来指明被分配的内存块的大小。得到的是pa内存块大小数据Airplane对象的内存AirplanepanewA而不是paAirplane对象的内存
  对于像Airplane这样很小的对象来说,这些额外的数据信息会使得动态分配对象时所需要的的内存的大小翻倍(特别是类里没有虚拟函数的时候)。
  如果软件运行在内存宝贵的环境中,就承受不起这种奢侈的内存分配方案了。为Airplane类专门写一个operatornew,就可以利用Airplane大小相等的特点,不必在每个分配的内存块上加上附带信息了。
  实现自定义operatornew思路:先让缺省operatornew分配一些大块的原始内存,每块的大小都足以容纳很多个Airplane对象。Airplane对象的内存块就取自这些大的内存块。当前没被使用的内存块被组织成链表称为自由链表以备未来Airplane使用。听起来好象每个对象都要承担一个next域的开销(用于支持链表),但不会:rep域的空间也被用来存储next指针(因为只是作为Airplane对象来使用的内存块才需要rep指针;同样,只有没作为Airplane对象使用的内存块才需要next指针),这可以用union来实现。具体实现:修改Airplane的定义,从而支持自定义的内存管理。classAirplane{修改后的类支持自定义的内存管理public:staticvoidoperatornew(sizetsize);。。。private:union{AirplaneR用于被使用的对象A用于没被使用的(在自由链表中)对象};指定一个大的内存块中放多少个Airplane对象,在后面初始化staticconstintBLOCKSIZE;大内存块大小staticAirplaneheadOfFreeL跟踪自由链表的表头表头指针声明为静态成员很重要,因为整个类只有一个自由链表,而不是每个Airplane对象都有。};
  下面该写operatornew函数了:voidAirplane::operatornew(sizetsize){把错误大小的请求转给::operatornew()处理;详见条款8if(size!sizeof(Airplane))return::operatornew(size);Airplanepp指向自由链表的表头headOfFreeLp若合法,则将表头移动到它的下一个元素if(p)headOfFreeLelse{自由链表为空,则分配一个大的内存块,可以容纳BLOCKSIZE个Airplane对象AirplanenewBlockstaticcast(::operatornew(BLOCKSIZEsizeof(Airplane)));将每个小内存块链接起来形成一个新的自由链表跳过第0个元素,因为它要被返回给operatornew的调用者for(inti1;iBLOCKSIZE1;i)newBlock〔i〕。nextnewBlock〔i1〕;用空指针结束链表newBlock〔BLOCKSIZE1〕。next0;p设为表的头部,headOfFreeList指向的内存块紧跟其后pnewBheadOfFreeListnewBlock〔1〕;}}
  如果你读了条款8,就会知道在operatornew不能满足内存分配请求时,需要有newhandler函数和异常有关的例行性动作之类的,只是::operatornew里面已经有这样的处理方案,
  Airplane类中不需要了。
  有了operatornew,下面要做的就是给出Airplane的静态数据成员的定义:AirplaneAirplane::headOfFreeL静态成员的初始值都被缺省设为0constintAirplane::BLOCKSIZE512;
  这个版本的operatornew将会工作得非常好。它为Airplane对象分配的内存要比缺省operatornew更少,而且运行得更快,可能会快2次方的等级。这没什么奇怪的,通用型的缺省operatornew必须应付各种大小的内存请求,还要处理内部外部的碎片;而你的operatornew只用操作链表中的一对指针。抛弃灵活性往往可以很容易地换来速度。
  下面我们将讨论operatordelete。还记得operatordelete吗?本条款就是关于operatordelete的讨论。但直到现在为止,Airplane类只声明了operatornew,还没声明operatordelete。想想如果写了下面的代码会发生什么:AirplanepanewA调用Airplane::operatornew。。。调用::operatordelete
  读这段代码时,如果你竖起耳朵,会听到飞机撞毁燃烧的声音,还有程序员的哭泣。问题出在operatornew(在Airplane里定义的那个)返回了一个不带头信息的内存的指针,而operatordelete(缺省的那个)却假设传给它的内存包含头信息。这就是悲剧产生的原因。
  这个例子说明了一个普遍原则:operatornew和operatordelete必须同时写(另一个理由,参见articleoncountingobjects一文的thesidebaronplacement章节)
  因而,继续设计Airplane类如下:classAirplane{public:。。。和前面的一样,只不过增加了一个,operatordelete的声明staticvoidoperatordelete(voiddeadObject,sizetsize);};传给operatordelete的是一个内存块,如果其大小正确,就加到自由内存块链表的最前面voidAirplane::operatordelete(voiddeadObject,sizetsize){if(deadObject0)见条款8if(size!sizeof(Airplane)){见条款8::operatordelete(deadObject);}Airplanecarcassstaticcast(deadObject);carcassnextheadOfFreeLheadOfFreeL}
  因为前面在operatornew里将错误大小的请求转给了全局operatornew(见条款8),那么这里同样要将错误大小的对象交给全局operatordelete来处理。如果不这样,就会重现你前面费尽心思想避免的那种问题new和delete句法上的不匹配。
  如果删除的对象是从没有虚析构函数的类继承而来的,那传给operatordelete的sizet值有可能不正确。这就是必须保证基类必须要有虚析构函数的原因。
  所有一切都很好,但从你皱起的眉头我可以知道你一定在担心内存泄露。有着大量开发经验的你不会没注意到,operatornew调用::operatornew得到了大块内存,但operatordelete却没有释放它们。内存泄露!内存泄露!
  但这里没有内存泄露!
  引起内存泄露的原因在于内存分配后指向内存的指针丢失了。如果没有垃圾处理或其他语言之外的机制,这些内存就不会被收回。但上面的设计没有内存泄露,因为它决不会出现内存指针丢失的情况。每个大内存块首先被分成Airplane大小的小块,然后这些小块被放在自由链表上。当客户调用Airplane::operatornew时,小块被自由链表移除,客户得到指向小块的指针。当客户调用operatordelete时,小块被放回到自由链表上。采用这种设计,所有的内存块要不被Airplane对象使用(这种情况下,是由客户来负责避免内存泄露),要不就在自由链表上(这种情况下内存块有指针)。所以说这里没有内存泄露。
  然而确实,::operatornew返回的内存块是没有被完全释放。但这个内存块叫内存池:内存泄漏会无限地增长,即使客户循规蹈矩;而内存池的大小决不会超过客户请求内存的最大值。
  修改Airplane的内存管理程序使得::operatornew返回的内存块在不被使用时自动释放并不难,但这里不会这么做,这有两个原因:第一个原因和自定义内存管理的初衷有关。
  自定义内存管理,最基本的一条是你确认缺省的operatornew和operatordelete使用了太多的内存或(并且)运行很慢。和采用内存池策略相比,跟踪和释放那些大内存块所写的每一个额外的字节和每一条额外的语句都会导致软件运行更慢,用的内存更多。在设计性能要求很高的库或程序时,如果你预计内存池的大小会在一个合理的范围之内,那采用内存池的方法再好不过了。第二个原因和处理一些不合理的程序行为有关。
  假设Airplane的内存管理程序被修改了,Airplane的operatordelete可以释放任何没有对象存在的大块的内存。那看下面的程序:intmain(){AirplanepanewA第一次分配:得到大块内存,生成自由链表,等内存块空;释放它panewA再次得到大块内存,生成自由链表,等内存块再次空,释放。。。你有了想法。。。return0;}
  这个糟糕的小程序会比用缺省的operatornew和operatordelete写的程序运行得还慢,占用还要多的内存,更不要和用内存池写的程序比了。
  当然有办法处理这种不合理的情况,但考虑的特殊情况越多,就越有可能要重新实现内存管理函数,而最后你又会得到什么呢?内存池不能解决所有的内存管理问题,在很多情况下是很适合的。
  实际开发中,你会经常要给许多不同的类实现基于内存池的功能。你会想,一定有什么办法把这种固定大小内存的分配器封装起来,从而可以方便地使用。是的,有办法。虽然我在这个条款已经唠叨这么长时间了,但还是要简单介绍一下,具体实现留给读者做练习。
  下面简单给出了一个Pool类的最小接口(见条款18),Pool类的每个对象是某类对象(其大小在Pool的构造函数里指定)的内存分配器。classPool{public:Pool(sizetn);为大小为n的对象创建一个分配器voidalloc(sizetn);为一个对象分配足够内存,遵循条款8的operatornew常规将p所指的内存返回到内存池;遵循条款8的operatordelete常规voidfree(voidp,sizetn);Pool();释放内存池中全部内存};
  这个类支持Pool对象的创建,执行分配和释放操作,以及被摧毁。Pool对象被摧毁时,会释放它分配的所有内存。这就是说,现在有办法避免Airplane的函数里所表现的内存泄漏似的行为了。然而这也意味着,如果Pool的析构函数调用太快(使用内存池的对象没有全部被摧毁),一些对象就会发现它正在使用的内存猛然间没了。这造成的结果通常是不可预测的。有了这个Pool类,即使Java程序员也可以不费吹灰之力地在Airplane类里增加自己的内存管理功能:classAirplane{public:。。。普通Airplane功能staticvoidoperatornew(sizetsize);staticvoidoperatordelete(voidp,sizetsize);private:AirplaneR指向实际描述的指针staticPoolmemPAirplanes的内存池};inlinevoidAirplane::operatornew(sizetsize){returnmemPool。alloc(size);}inlinevoidAirplane::operatordelete(voidp,sizetsize){memPool。free(p,size);}为Airplane对象创建一个内存池,在类的实现文件里实现PoolAirplane::memPool(sizeof(Airplane));
  这个设计比前面的要清楚、干净得多,因为Airplane类不再和非Airplane的代码混在一起。union,自由链表头指针,定义原始内存块大小的常量都不见了,它们都隐藏在它们应该呆的地方Pool类里。让写Pool的程序员去操心内存管理的细节吧,你只是让Airplane类正常工作。
  现在应该明白了,自定义的内存管理程序可以很好地改善程序的性能,而且它们可以封装在象Pool这样的类里。但请不要忘记主要的一点,operatornew和operatordelete需要同时工作,那么你就写了operatornew,就也一定要写operatordelete。
投诉 评论 转载

肺炎宅家闲得快发霉了!全靠这些游戏撑过去俗话说得好,得不到的才是最好的。在从前总想着有时间就能天天宅在家,现在新冠肺炎疫情下,这一愿望被迫成真,却发现远不如想象中那么美好。每天都卧室散步客厅放风,看着窗外空荡荡的大街……EffectiveC条款10如写了operatornew就要为什么有必要写operatornew和operatordelete?答案通常是:为了效率。缺省的operatornew和operatordelete具有非常好的通用性,它……打通网络游戏任督二脉,华硕WiFi6电竞路由器RTAX82U随着WiFi6概念的逐渐普及,各大路由器厂商开始逐步完善旗下WiFi6无线路由器的产品线布局,而华硕最近推出的RTAX82U就是一款定位千元级的AX5400双频WiFi6无线电……英雄联盟出现平衡性漏洞部分英雄可造成全图伤害IT之家6月19日消息根据《英雄联盟》官方的消息,当前游戏内召唤师峡谷模式存在平衡性漏洞,导致部分英雄在特殊情况下可以对全地图范围内的敌人造成伤害。目前工程师正在对该问题进行紧……4月中国手游发行商全球收入排行腾讯网易前二,哔哩哔哩第13IT之家5月8日消息今日,移动应用数据分析平台SensorTower发布了最新研究报告,带来了2020年4月中国手游发行商全球AppStore和GooglePlay收入排行。……如何判断windows启动方式是UEFI还是BIOS?你应该是想问,windows的启动方式是UEFI还是legacy吧目前的BIOS还是都支持UEFI和legecy的下面有两个简单的辨别方法,方法一可100区分清楚。方法一:……有没有什么帮助学习的app?有时候,想要投入学习,提高效率需要借助一些工具。像是一些记性不好的同学,想要进行时间管理,最好还是把计划要做的事情列一个清单。今天范叔给你们介绍一些我自己正在用的实用AP……冷饭真香!生化危机3重制版Steam试玩版获特别好评IT之家3月20日消息3月20日凌晨《生化危机3:重制版》试玩版在Steam上解锁,目前该试玩版在Steam上已经获得特别好评的评价。有玩家表示,玩完《生化危机3:重制版》试玩……生化危机3重制版新吉尔原型确认俄罗斯女模IT之家12月22日消息不久前,《生化危机3:重制版》Steam版本已经开启预购,价格为414元,将于2020年4月3日正式推出。在重制版中,JillValentine(吉尔瓦……生化危机3重制版上架Steam,价格为414元IT之家12月18日消息据Steam游戏平台消息,《生化危机3:重制版》Steam版本已经开启预购,价格为414元;将于2020年4月3日正式推出。据了解,《生化危机3》……都说G4560猛,自己用过才知道有多猛京东是个奇葩的网站,大多数时候享受着众人的谩骂。不过,有时候也会出现像BUG一样的捆绑套餐。最近RYZEN系列翻身成功,很多朋友再次回归AMD的怀抱。真是风水轮转,平时都……在华为手机中,哪款手机的性价比较高?华为如今最强的芯片莫过于麒麟990了,虽然有不少5G手机都是搭载这款麒麟990处理器的,但是因为其它配置的相差,所以在价格上略有悬殊。其中,就有一款手机,我认为在麒麟990芯片……
七级总理新加坡旅游局将加强对牛车水人流管控梅江镇开展2021年国家网络安全宣传周活动独生子女证有什么用(独生子女证用处大吗)出现了一个问题导致程序停止正常工作(软件一打开就停止工作)较劲模范夫妻(模范夫妻上)什么是内卷(内卷的最通俗解释)科普下现在进行时的构成是什么及现在进行时使用场合教师节送礼(给老师送礼送啥比较好)黄河堤坝上这些不起眼的小花,连农村长大的孩子也认不全!女人如何保养自己(女人如何保养自己的皮肤?)狗的寿命有多长(土狗的寿命有多长)姜夔简介压岁钱华山论屎水浒转音乐之都维也纳教学反思7号我的姐姐4。99亿成最大赢家,而哥斯拉将破十亿离婚房产更名需要什么手续宝可梦奇闻趣事大木博士的扮演设定,并非招式的对战命令在许多个时候你应微笑公司旅游的通知数码知识小米关机密码怎么设置怎么设置关机要密码4种食物助你生一个健康宝宝

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找美丽时装彩妆资讯历史明星乐活安卓数码常识驾车健康苹果问答网络发型电视车载室内电影游戏科学音乐整形