您好,欢迎访问三七文档
当前位置:首页 > IT计算机/网络 > 其它相关文档 > c++容器使用经验总结
c++容器使用经验总结第1章容器第1条:慎重选择容器类型。标准STL序列容器:vector、string、deque和list。标准STL关联容器:set、multiset、map和multimap。非标准序列容器slist和rope。slist是一个单向链表,rope本质上是一“重型”string。非标准的关联容器hash_set、hase_multiset、hash_map和hash_multimap。vectorchar作为string的替代。(见第13条)vector作为标准关联容器的替代。(见第23条)几种标准的非STL容器,包括数组、bitset、valarray、stack、queue和priority_queue。你是否关心容器中的元素是如何排序的?如果不关心,选择哈希容器.容器中数据的布局是否需要和C兼容?如果需要兼容,就只能选择vector。(见第16条)元素的查找速度是否是关键的考虑因素?如果是,就要考虑哈希容器、排序的vector和标准关联容器-或许这就是优先顺序。对插入和删除操作,你需要事务语义吗?如果是,只能选择list。因为在标准容器中,只有list对多个元素的插入操作提供了事务语义。deque是唯一的、迭代器可能会变为无效(插入操作仅在容器末尾发生时,deque的迭代器可能会变为无效)而指向数据的指针和引用依然有效的标准STL容器。第2条:不要试图编写独立于容器类型的代码。如果你想编写对大多数的容器都适用的代码,你只能使用它们的功能的交集。不同的容器是不同的,它们有非常明显的优缺点。它们并不是被设计用来交换使用的。你无法编写独立于容器的代码,但是,它们(指客户代码)可能可以。第3条:确保容器中的对象拷贝正确而高效。copyin,copyout,是STL的工作方式,它总的设计思想是为了避免不必要的拷贝。使拷贝动作高效并且防止剥离问题发生的一个简单办法是使容器包含指针而不是对象。第4条:调用empty而不是检查size()是否为0。理由很简单:empty对所有的标准容器都是常数时间操作,而对一些list的实现,size耗费线性时间。第5条:区间成员函数优先于与之对应的单元素成员函数。区间成员函数写起来更容易,更能清楚地表达你的意图,而且它们表现出了更高的效率。第6条:当心C++编译器最烦人的分析机制。把形参加括号是合法的,把整个形参的声明(包括数据类型和形参名字)用括号括起来是非法的。第7条:如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉。STL很智能,但没有智能到知道是否该删除自己所包含的指针所指向的对象的程度。为了避免资源泄漏,你必须在容器被析构前手工删除其中的每个指针,或使用引用计数形式的智能指针(比如Boost的sharedprt)代替指针。第8条:切勿创建包含auto_ptr的容器对象。拷贝一个auto_ptr意味着改变它的值。例如对一个包含auto_ptr的vector调用sort排序,结果是vector的几个元素被置为NULL而相应的元素被删除了。第9条:慎重选择删除元素的方法。要删除容器中指定值的所有对象:如果容器是vector、string或deque,则使用erase-remove习惯用法。SeqContainerintc;c.erase(remove(c.begin(),c.end(),1963),c.end());如果容器是list,则使用list::remove。如果容器是一个标准关联容器,则使用它的erase成员函数。要删除容器中满足特定条件的所有对象:如果容器是vector、string或deque,则使用erase-remove_if习惯用法。如果容器是list,则使用list::remove_if。如果容器是一个标准关联容器,则使用remove_copy_if和swap,或者写一个循环遍历容器的元素,记住当把迭代器传给erase时,要对它进行后缀递增。AssocCOntainerintc;...AssocContainerintgoodValues;remove_copy_if(c.begin(),c.end(),inserter(goodValues,goodValues.end()),badValue);c.swap(goodValues);或for(AssocContainerint::iteratori=c.begin();i!=c.end();/*donothing*/){if(badValue(*i))c.erase(i++);else++i;}要在循环内部做某些(除了删除对象之外的)操作:如果容器是一个标准序列容器,则写一个循环来遍历容器中的元素,记住每次掉用erase时,要用它的返回值更新迭代器。如果容器是一个标准关联容器,则写一个循环来遍历容器中的元素,记住每次把迭代器传给erase时,要对迭代器做后缀递增。第10条:了解分配子(allocator)的约定和限制。第11条:理解自定义分配子的合理用法。第12条:切勿对STL容器的线程安全性有不切实际的依赖。对一个STL实现你最多只能期望:多个线程读是安全的。多个线程对不同的容器写入操作是安全的。你不能期望STL库会把你从手工同步控制中解脱出来,而且你不能依赖于任何线程支持。第2章vector和string第13条:vector和string优先于动态分配的数组。如果用new,意味着你要确保后面进行了delete。如果你所使用的string是以引用计数来实现的,而你又运行在多线程环境中,并认为string的引用计数实现会影响效率,那么你至少有三种可行的选择,而且,没有一种选择是舍弃STL。首先,检查你的库实现,看看是否可以禁用引用计数,通常是通过改变某个预处理变量的值。其次,寻找或开发一个不使用引用计数的string实现。第三,考虑使用vectorchar而不是string。vector的实现不允许使用引用计数,所以不会发生隐藏的多线程性能问题。第14条:使用reserve来避免不必要的重新分配。通常有两种方式来使用reserve以避免不必要的重新分配。第一种方式是,若能确切知道或大致预计容器中最终会有多少个元素,则此时可使用reserve。第二种方式是,先预留足够大的空间,然后,当把所有的数据都加入后,再去除多余的容量。第15条:注意string实现的多样性。如果你想有效的使用STL,那么你需要知道string实现的多样性,尤其是当你编写的代码必须要在不同的STL平台上运行而你又面临着严格的性能要求的时候。第16条:了解如何把vector和string数据传给旧的API。如果你有个vectorv,而你需要得到一个只想v中的数据的指针,从而可把数据作为数组来对才,那么只需要使用&v[0]就可以了,也可以用&*v.begin(),但是不好理解。对于strings,随应的形式是s.c_str()。如果想用来自CAPI的数据来初始化一个vector,那么你可以利用vector和数组的内存布局兼容性,先把数据写入到vector中,然后把数据拷贝到期望最终写入的STL容器中。第17条:使用“swap技巧”出去多余的容量。vectorContestant(contestants).swap(contestants);表达式vectorContestant(contestants)创建一个临时的矢量,它是contestants的拷贝:这是由vector的拷贝构造函数来完成的。然而,vector的拷贝构造函数只为所拷贝的元素分配所需要的的内存,所以这个临时矢量没有多余的容量。然后我们把临时矢量中的数据和contestants中的数据作swap操作,在这之后,contestants具有了被去除之后的容量,即原先临时变量的容量,而临时变量的容量则变成了原先contestants臃肿的容量。到这时,临时矢量被析构,从而释放了先前为contestants所占据的内存。同样的技巧对string也实用:strings;...string(s).swap(s);第18条:避免使用vectorbool。作为STL容器,vectorbool只有两点不对。首先,它不是一个STL容器;其次,它并不存储bool。除此以外,一切正常。因此最好不要使用它,你可以用dequebool和bitset替代。vectorbool来自一个雄心勃勃的试验,代理对象在C++软件开发中经常会很有用。C++标准委员会的人很清楚这一点,所以他们决定开发vectorbool,以演示STL如果支持“通过代理对象来存取其元素的的容器”。他们说,C++标准中有了这个例子,于是,人们在实现自己的基于代理的容器时就有了一个参考。然而他们却发现,要创建一个基于代理的容器,同时又要求它满足STL容器的所有要求是不可能的。由于种种原因,他们失败了的尝试被遗留在标准中。第3章关联容器第19条:理解相等(equality)和等价(equivalence)的区别。标准关联容器总是保持排列顺序的,所以每个容器必须有一个比较函数(默认为less)。等价的定义正是通过该比较函数而确定的。相等一定等价,等价不一定相等。第20条:为包含指针的关联容器指定比较类型。每当你创建包含指针的关联容器时,容器将会按照指针的值(就是内存地址)进行排序,绝大多数情况下,这不是你所希望的。第21条:总是让比较函数在等值情况下返回false。现在我给你演示一个很酷的现象。创建一个set,用less_equal作为它的比较类型,然后把10插入到该集合中:setint,less_equalints;//s用=来排序s.insert(10);s.insert(10);对于第二个insert,集合会检查下面的表达式是否为真:!(10a=10b)&&!(10b=10a);//检查10a和10b是否等价,结果是!(true)&&!(true)为false结果集合中有两个10!从技术上讲,用于对关联容器排序的比较函数必须为他们所比较的对象定义个“严格的弱序化”(strictweakordering)。第22条:切勿直接修改set或multiset中的键。如果你不关心可移植性,而你想改变set或multiset中元素的值,并且你的STL实现(有的STL实现中,比如setT::iterator的operator*总是返回constT&,就不能修改了)允许你这么做,则请继续做下去。只是注意不要改变元素中的键部分,即元素中能够影响容器有序性的部分。如果你重视可移植性,就要确保set和multiset中的元素不能被修改。至少不能未经过强制类型转换(转换到一个引用类型const_castT&)就修改。如果你想以一种总是可行而且安全的方式来许该set、multiset、map和multimap中的元素,则可以分5个简单步骤来进行:1.找到你想修改的容器的元素。如果你不能肯定最好的做法,第45条介绍了如何执行一次恰当的搜索来找到特定的元素。2.为将要被修改的元素做一份拷贝,。在map和multimap的情况下,请记住,不要把该拷贝的第一个部分声明为const。毕竟,你想要改变它。3.修改该拷贝,使它具有你期望的值。4.把该元素从容器中删除,通常是通过erase来进行的(见第9条)。5.把拷贝插到容器中去。如果按照容器的排列顺序,新元素的位置可能与被删除元素的位置相同或紧邻,则使用“提示”(hint)形式的insert,以便把插入的效率从对数时间提高到常数时间。把你从第1步得来的迭代器作为提示信息。第23条:考虑用排序的vector替代关联容器。标准关联容器通常被实现为平衡的二叉查找树。也就是说,它所适合的那些应用程序首先做一些插入操作,然后做查找,然后可能又插入一些元素,或许接着删掉一些,随后又做查找,等等。这一系列时间的主要特征是插入、删除和超找混在一起。总的来说,没办法预测出针对这颗树的下一个操作是什么。很多应用程
本文标题:c++容器使用经验总结
链接地址:https://www.777doc.com/doc-3357980 .html