您好,欢迎访问三七文档
当前位置:首页 > 临时分类 > effectivestl
1.确保容器中的对象拷贝正确而高效一旦一个对象被拷贝到容器,它经常会被进一步被拷贝:例如Vector,string,deque。而拷贝,其实很简单,就是利用一个对象的拷贝成员函数。当然在拷贝的过程中,当存在继承关系时,我们需要注意拷贝动作而导致的剥离。也就是说,当你创建一个存放基类对象的容器时,却向其中插入派生类的对象,那么派生类的对象所持有的特有部分将会丢失。而防止这种情况的产生就是使用Widget*的容器,2,,调用empty而不是检查size()是不是0对任何容器上使用if(c.size()==0)....本质上与if(c.empty()).....是等价的。为什么使用empty:empty对所有的标准容器都是常数时间操作,而对一些list的实现,size耗费线性时间(在于list所独有的splice操作)。3.区间成员函数优先于与之对应的单元素成员函数优势:1使用区间成员函数可以减少代码2.使用区间成员函数通常会使意图清晰,代码直接。在能应用STL时,尽量避免自己写函数,例如显示的循环。4.注意C++编译器最烦人的分析机制假设有一个存有int的文件,想把这些整数复制到一个list中,错误做法:IfstreamdataFile(“ints。Dat”);listintdata(istream_iteratorint(dataFile),istream_iteratorint());编译器最后会编译成:声明一个函数data,其返回值是listint,有两个参数,第一个参数是dataFile,类型为Istream_iterator,dataFile两边的括号会被忽略;第二个参数没名称。又例如:ClassWidget{};Widgetw();它没声明一个名为w的Widget,而是声明一个名为w的函数,该函数不带参数,并返回一个Widget。正确的做法:IfstreamdataFile(“ints.dat”);Istream_iteratorintdatabigen(dataFile);Istream_iteratorintdataend;Listintdata(databigen,dataend);虽然使用命名的迭代器对象与通常的STL程序风格相违背,但这样能使编译器无二义性。5.如果容器中包含了通过new创建的指针,切记在容器对象析构前将指针delete掉原因:当容器包含的是通过new的方式而分配的指针时,指针容器在自己被析构时会析构所包含的每个元素,但指针的“析构函数”不做任何事,当然也不会delete。也就是说通过new的范式分派的指针,我们需要自己去手动delete。例子:下面代码直接导致资源泄漏:Voiddosomething(){Vectorwidget*vwp;For(inti=0;iNumber;++i)vwp.push_back(newwidget);.........}当vwp结束时,通过new创建的对象没被删除。一般做法:voiddosomething(){Vectorwidget*vwp;......for(vectorwidget*::iteratori=vwp.begin();i!=vwp.end();i++)delete*i;}进阶做法:利用STL中的for_each替代for循环做的事情。首先创建彷函数,也就是函数对象:TemplatetypenameTStructDeleteObject:publicunary_functionconstT*,void{可配接的关键Voidoperator()(constT*ptr)const{Deleteptr;}};Voiddosomething(){......For_each(vwp.begin(),vwp.end,DeleteObjectwidget());}注意:这样从开始就不好,当指明DeleteObject要删除的是widget,而vwp是widget*。假设从string继承:Classspecialstring:publicstring{....}String没有虚构函数,而从没有虚构函数的类进行共有继承是一项禁忌。Voiddosomething(){Dequespecilalstring*dssp;.....For_each(dssp.begin,dssp.end,DeleteObjectstring());错误很明显了》直接而安全的做法:StructDeleteObject{TemplatetypenameTVoidoperator()(constT*ptr){自动实例化了operater()Deleteptr;}};Voiddosomething(){Dequespecilalstring*dssp;.....For_each(dssp.begin,dssp.end,DeleteObject());最佳的做法:利用shared_ptr智能指针Voiddosomething(){Typedefboost::shared_ptrwidgrtspw;切记通过auto_ptr的容器时指针自动删除Vectorspwvwp;For(inti=0;inumbers.i++){Vwp.push_back(spw(newwidget));.....}6.切勿创建包含auto_ptr的容器对象注意:当你复制一个auto_ptr时,它所指的对象的所有权被移交到复制的auto_ptr上,而它自身被置为Null。例如:Auto_ptrwidgetpw1(newwidget);Auto_ptrwidgetpw2(pw1);pw2指向pw1的widget,pw1被置为nullPw1=pw2;现在pw1指向widget,pw2被置为nullAuto_ptr的这个特性将导致一些奇怪的现象,例如sort排序。而正确的选择是使用boost中的shared_ptr智能指针。总之,在STL中,警惕使用auto_ptr这个智能指针。7.慎重选择删除元素的方法一:删除容器中有特定值的所有对象1,如果容器是vector,string,deque,则使用erase-remove习惯用法。先回顾一下remove,它只移除但不删除。也就是说remove从容器中删除元素,但是容器的元素数目却不会减少。看下remove源代码:TemplateclassInputIterator,classOutIterator,classTOutputIteratorremove_copy(InputIteratorfirst,InputIteratorlast,OutputIteratorresult,constT&value){For(;first!=last;++first){if(*first!=value){*result=*first;++result;}Returnresult;}}TemplateclassForwardIterator,classTForwardIteratorremove(ForwardIteratorfirst,ForwardIteratorlast,constT&value){first=find(first,last,value);ForwardIteratornext=first;returnfirst==last?first:remove_copy(++next,last,first,value)}返回一个新的end。很明显,remove没法删除元素。顺带一下,array不适合使用remove,因为array无法缩小尺寸,导致残余数据永远存在,对于array,remove_copy比较好。所以要删除的话就需要erasevectorintv;.....v.erase(remove(v.begin(),v.end(),value),v.end());......当然,对包含指针的容器使用remove这类算法时需要特别注意,后面讲.2.如果容器是list,则使用list::remove。原因:容器(尤其是关联容器)的成员函数一般优先于同名的算法:一:成员函数往往速度跟快二:成员函数与容器结合的更加紧密。例如:同样使用find函数,由于关联容器是基于红黑树形成的,使用关联容器的find函数是对数寻找,而find算法是一个过去,为o(n)。此为,为什么list删除时就remove就好呢,它比较特别:看下源代码:TemplateclassT,classAllocVoidlistT,Alloc::remove(constT&value){Iteratorfirst=begin();Iteratorlast=end();While(fierst!=last){Iteratornext=first;If(*first==value)erase(first);first=next;}3.如果容器是一个标准关联容器,则使用它的erase成员函数为什么标准关联容器不能使用remove:关联容器没有remove成员函数,而使用remove算法可能会覆盖容器的值,同时可能破会容器(STL中set的iterator是const的,而map中的pair的key是const)。二.删除容器中满足特定判别式的所有对象。1.如果容器是vector,string,deque,则直接使用erase_remove_if的习惯用法2.如果容器是list,则使用list::remove_if3.如果容器是关联容器,则使用remove_copy_if和swap,或者写一个循环来遍历方法一:设置一个临时对象来保存AssocContainerintc;c是一个关联容器.......AssocContainerintgoodvalues;Remove_copy_if(c.begin(),c.end(),inserter(goodvalues,goodvalues.end()),badvalue);c.swap(goodValues);代价:需要复制所有不需要不被删除的元素。方法二:循环错误做法:AssocContainintc;.....for(AssocContainint::iteratori=c.begin();i!=c.end();i++){.if(badValue(*i))c.erase(i)原因:关联容器的erase函数返回类型为void;也就是说c.erase(i)返回,i就成无效值了,在下个循环i++就没法执行了。正确做法:AssocContainintc;.....for(AssocContainint::iteratori=c.begin();i!=c.end();){.if(badValue(*i))c.erase(i++);递增迭代器以保持其有效性elsei++;}原因:表达式i++的值是i的旧值,而作为副作用,i被递增。这样传给erase的是i的旧值,但在erase执行前也进行了递增i(++i行不?)。对于vector,string,deque也使用循环呢?for(SeqContainint::iteratori=c.begin();i!=c.end();){.if(badValue(*i))c.erase(i++);错误:应为erase返回的类型是iterator,是i的下个值elsei++;}例如vector的erase:Iteratorerase(iteratorposition){If(position+1!=end())copy(position+1,finish,position);--finish;destroy(finish);Reeturnposition;}正确做法:for(SeqContainint::iteratori=c.begin();i!=c.end();){.if(badValue(*i))i=c.eras
本文标题:effectivestl
链接地址:https://www.777doc.com/doc-2911175 .html