Effective STL
xupeng1644
道阻且长,行则将至!
展开
-
50. 熟悉与STL相关的Web站点
SGI STL站点 http://www.sgi.com/tech/stlSTLport站点 http://www.stlport.orgBoos站点 http://www.boost.org原创 2019-11-22 17:21:11 · 1372 阅读 · 0 评论 -
49. 学会分析与STL相关的编译器诊断信息
总结归纳如下:1 将冗长的模板类型名替换成短的名字,总是可以简化编译器的诊断信息。2 vector和string的迭代器通常就是指针,所以当错误地使用了iterator的时候,编译器的诊断信息中可能会引用到指针类型。3 如果诊断信息中提到了back_insert_iterator、front_insert_interator或者insert_iterator,则几乎总是意味着错误地调用了ba...原创 2019-11-22 14:33:29 · 1360 阅读 · 0 评论 -
48. 总是包含(#include)正确的头文件
在一个STL平台上能够编译通过的软件,在另外一个平台上可能需要一些额外的#include指令才能通过编译。你不能将移植的困难归咎于编译器或者类库实现,如果你漏掉了必要的头文件,那么这是自己的错。任何时候只要你引用了std名字空间中的元素,你就由责任包含正确的头文件。如果省略了这些头文件,可能在特定的STL平台上可以通过编译,但是其他平台完全有可能拒绝编译通过。下面是几条提示:几乎所...原创 2019-11-21 17:29:57 · 1290 阅读 · 0 评论 -
47. 避免产生"直写型"(write-only)的代码
什么叫直写型代码?编写代码的时候,它看似非常直接和简洁,因为他是由某些基本想法自然而形成的。然后,阅读代码的人却很难将最终的语句还原成它所依据的思路。虽然很容器一编写,但是难以阅读和理解。比如下面代码:std::vector<int> datas;int x, y;...datas.erase( std::remove_if(std::find_if(datas.rbeg...原创 2019-11-20 19:34:39 · 5722 阅读 · 0 评论 -
46. 考虑使用函数对象而不是函数作为STL算法的参数
原因如下:性能优势对于排序算法,使用函数对象编译器可以直接进行内联,减少函数调用次数。而使用普通函数时,传入算法内部的实际是函数指针,编译器无法对齐进行优化。编译器原因必须让你的程序正确通过编译,由于种种原因,STL平台可能会拒绝完全合法的代码,这种情况并不罕见。原因可能是编译器的缺陷,也可能是STL库的原因,或者两者兼而有之。优先使用函数对象,有助于避免一些微妙的、语言本身...原创 2019-11-20 19:20:37 · 1347 阅读 · 0 评论 -
45. 正确区分count、find、binary_search、lower_bound、upper_bound和equal_range
一张表足以说明一切:原创 2019-11-20 18:35:00 · 1344 阅读 · 0 评论 -
44. 容器的成员函数优先于同名的算法
有些STL 容器提供了一些与算法同名的成员函数。大多数情况下,应该使用这些成员函数,而不是相应的STL算法。有两个理由:成员函数往往速度快。成员函数通常与容器结合地更紧密,这是算法所不能比的。set容器的find成员函数以对数时间运行,而find算法以线性时间运行。效率并不是find成员函数和find算法之间的唯一差别。STL算法以相同性而判断两个对象是否具有相同的值,而关联容器使用...原创 2019-11-16 15:30:53 · 1417 阅读 · 0 评论 -
43. 算法调用优先于手写的循环
STL算法涉及面很广,这意味着你本该编写循环来完成的任务也可以用STL算法来完成。例如,有一个Widget类:class Widget{public: ... void redraw() const; ...};当你想在一个包含Widget的list中调用所有成员的Widget对象,可以用一个循环来完成,std::list<Widget> datas;...f...原创 2019-11-16 14:55:08 · 1404 阅读 · 0 评论 -
42. 确保"lessT"与"operator小于"具有相同的语义
Widget类定义如下:class Widget{public: ... size_t weight() const; size_t maSpeed() const; ...}通常情况下,按重量对Widget进行排序是最自然的方式。Widget的operator < 反映了这一点。bool operator < (const Widget& lhs, co...原创 2019-11-14 20:46:26 · 1491 阅读 · 0 评论 -
41. 理解ptr_fun、mem_fun和mem_fun_ref的来由
如果有一个函数f和一个对象x,现在希望在x上调用f,而我们在x的成员函数之外,执行这个调用,C++提供了三种方法:f(x); // 语法1 f是一个非成员函数x.f();// 语法2 f时成员函数,x是一个对象或对象的引用p->f(); // 语法3 f是成员函数,p是一个对象指针有一个用于测试Widget对象的函数Test和一个存放Widget的容器datas:void Te...原创 2019-11-14 13:08:49 · 1556 阅读 · 0 评论 -
40. 若一个类是函数子,则应该使它可配接
函数子即函数对象。如下所示:class Widget{public: bool IsBigVal(); int GetValue(); ...}struct IsInteresting{ bool operator() (const Widget& one) { return one.IsBigValue(); }}在一个存储Widget的vecto...原创 2019-11-14 11:03:35 · 1370 阅读 · 0 评论 -
39. 确保判别式是"纯函数"
判别式(predicate):返回为bool类型(或者可以隐式转换成bool类型)的函数。在STL中,判别式有着广泛的用途。标准关联容器的比较函数就是判别式:对于像find_if以及各种与排序有关的算法,判别式往往也被作为参数来传递。纯函数(pure function):返回值仅仅以依赖于其参数的函数。例如,假设f是一个纯函数,x和y是两个对象,那么只有当x或者y的值发生变化的时候,f(x,...原创 2019-11-12 19:17:22 · 1453 阅读 · 0 评论 -
38. 遵循按照值传递的原则来设计函数子类
由于函数对象往往会按照值传递和返回,所以,你必须确保你编写的函数对象在经历了传递之后还能正常工作。这意味两件事:你的函数对象必须尽可能小,否则拷贝的考校会非常昂贵。函数对象必须是单态的,也就是说不能使用虚函数。这是因为,如果参数的类型是基类类型,而实参是派生类对象,那么在传递过程中会产生剥离问题。在对象拷贝过程中,派生部分可能会被去掉,而仅留下基类部分。...原创 2019-11-12 12:47:16 · 1355 阅读 · 0 评论 -
37. 使用accumulate或者for_each进行区间统计
accumulate和for_each在两个方面有所不同。名字accumulate暗示着这个算法将会计算出一个区间的统计信息。而for_each听起来就好像是对一个区间的每个元素做一个操作,用for_each来统计一个区间是合法的,但是不如accumulate来得清晰。accumulate直接返回我们索要的统计结果,而for_each却返回一个函数对象,我们必须从这个函数对象中提取出我...原创 2019-11-11 10:49:02 · 1495 阅读 · 0 评论 -
36. 理解copy_if算法的正确实现
STL中有11个算法名字包含"copy":copycopy_backwardreplace_copyreverse_copyreplace_copy_ifunique_copyremove_copyrotate_copyremove_copy_ifpartial_sort_copyunitialized_copy但是STL中却不包括copy_if的实现,如果需要它,必须...原创 2019-11-08 09:22:40 · 1776 阅读 · 0 评论 -
35. 通过mismatch或lexicographical_compare实现简单的忽略大小写的字符串比较
略原创 2019-11-06 21:33:01 · 1338 阅读 · 0 评论 -
34.了解那些算法要求使用排序的区间作为参数
要求排序区间的算法有:binary_searchlower_boundupper_boundequal_rangeset_unionset_interationset_differenceset_symmertric_differencemergeinplace_mergeincludesuniqueunique_copyunique和unique_copy并不要...原创 2019-11-06 21:28:23 · 1376 阅读 · 0 评论 -
33. 对包含指针的容器使用remove这一类算法时要特别小心
接着前面条款32的例子:容器中的元素为 1 2 3 4 3 5,调用remove算法删除的元素为3,容器中元素会变成:1 2 4 5 3 5。之前容器中的第一个3将会被直接覆盖掉。想想如果这些都是堆内存指针,同时这些指针没有其他拷贝,那么就会出现内存泄露,因为已经没有指针指向3指向的堆内存。class widget{public: ... bool IsCertified() c...原创 2019-11-06 20:45:35 · 1418 阅读 · 0 评论 -
32. 如果确实需要删除元素,则需要在remove这一类算法之后调用erase
从容器中删除元素,唯一的办法是调用容器的成员函数,几乎总是erase的某种形式。因为从容器中删除元素的唯一方法是调用容器的成员函数,而remove算法并不知道它操作的元素所在的容器,所以remove不可能从容器中删除元素。使用remove算法从容器中删除元素,容器中的元素不会因此而减少。remove算法不是真正意义上的删除,因为它做不到。remove算法移动了区间中的元素,“不用被删...原创 2019-11-06 13:22:31 · 1529 阅读 · 0 评论 -
31. 了解各种与排序有关的选择
排序算法有如下:sort、stable_sortpartial_sort、无对应的稳定排序算法nth_element、无对应的稳定排序算法partition、stable_partitionsort、stable_sort、partitial_sort、nth_element算法都要求随机访问迭代器,所以这些算法都只能被应用于vector、string、deque和数组。对标准关联容...原创 2019-11-04 13:05:36 · 1410 阅读 · 0 评论 -
30. 确保目标空间足够大
以一个例子开始讨论:int transmogrify(int x); // 该函数更具x生成一个新的值std::vector<int> values;...std::list<int> results;std::transform(values.begin(), values.end(), results.end(), transmogrify); // tr...原创 2019-11-04 12:28:32 · 1431 阅读 · 0 评论 -
29. 对于逐个字符的输入请考虑使用istreambuf_iterator
无原创 2019-10-31 10:07:55 · 1477 阅读 · 1 评论 -
28. 正确理解由reverse_iterator的base()成员函数所产生的iterator的用法
代码如下:std::vector<int> datas;datas.reverse(5);for (int idx = 0; idx < 5; ++idx) datas.push_back(idx);std::vector<int>::reverse_iterator ri = std::find(datas.rbegin(), datas.rend(), 3...原创 2019-10-31 09:57:46 · 1556 阅读 · 0 评论 -
27. 使用distance和advance将容器的const_iterator转换成iterator
想要将一个const_iterator装换成iterator供相关函数调用,直观的做法是使用const_cast,如下所示:typedef std::deque<int> IntDeque;typedef IntDeque::iterator Iter;typedef IntDeque::const_iterator ConstIter;constIter ci;...I...原创 2019-10-30 13:01:05 · 1512 阅读 · 0 评论 -
26. iterator优先于const_iterator、reverse_iterator以及const_reverse_iterator
理由如下:有些版本的insert和erase函数要求使用iterator。如果你需要调用这些函数,那你就必须使用iterator。const和reverse类型的迭代器不能满足这些函数的要求。要想隐式地将一个const_iterator装换成iterator是不能的。从reverse_iterator装换而来的iterator在使用之前可能需要相应的调整。由此可见,尽量使用itera...原创 2019-10-29 23:36:39 · 1659 阅读 · 0 评论 -
25. 熟悉非标准的哈希容器
C++ 11中已经引入了hash容器包括:unordered_setunordered_multisetunordered_mapunordered_multimap注意hash容器与关联容器的区别:hash容器是基于hash函数的,hash容器对相同元素的判断是基于相等,使用的排序规则默认为std::equal_to<>。其元素不是以排序方式存放的。关联容器...原创 2019-10-29 18:24:09 · 1357 阅读 · 0 评论 -
24. 当效率至关重要时,请在map::operator[]与map::insert之间谨慎作出选择
有一个widget类如下:class Widget{public: Widget(); Widget(double weight); Widget& operator = (double weight);}使用如下:std::map<int, Widget> datas; // 语句1datas[1] = 1.50; // 语句2调用语句2之前,dat...原创 2019-10-28 22:55:38 · 1594 阅读 · 0 评论 -
23. 考虑用排序的vector替代关联容器
当想到排序时,第一个想到的关联容器时set、muliset、map、multimap, 它们的底层实现为红黑树,查找的时间复杂度为对数时间复杂度。也可以使用哈希容器unordered_set、unorderd_multiset、unordered_map、unordered_multimap,它们的底层实现为哈希表,查找的时间复杂度为常数时间复杂度。关联容器使用的红黑树,优化了插入、查找、删除等...原创 2019-10-28 16:36:17 · 1060 阅读 · 0 评论 -
22. 切勿直接修改set或multiset中的键
像所有的标准关联容器一样,set和multiset按照一定的顺序来存放自己的元素,而这些容器的正确行为也是建立在其原始保存有序的基础之上的。如果把关联容器中的一个元素的值改变了,那么,新的值可能不在正确的位置上,这将会打破容器的有序性。直接修改map、multimap的值,不能通过编译,因为他们的元素类型实际为std::pair<const K, V>,因为键的类型时const K,...原创 2019-10-28 12:34:08 · 1602 阅读 · 0 评论 -
21. 总是让比较函数在等值情况下返回false
有例子如下:std::set<int, std::less_equal<int>> datas;datas.insert(10); // 10adatas.insert(10); // 10b称第一次插入的10为10a,第二次插入的为10b。按照设想,10b不能插入到datas中,因为10b的值也是10。结果却是能够插入。注意,关联容器“相同”的定义是等价,10...原创 2019-10-26 23:34:25 · 1620 阅读 · 0 评论 -
20. 为包含指针的关联容器指定比较类型
有如下代码:std::set<std::string*> datas;datas.insert(new std::string("Anteater"));datas.insert(new std::string("Wombat"));datas.insert(new std::string(""Lemur));datas.insert(new std::string("Pen...原创 2019-10-26 22:31:06 · 1321 阅读 · 0 评论 -
19. 理解相等(equality)和等价(equivalence)的区别
STL中,对两个对象进行比较,看他们的值是否相同,涉及到两个概念,相等和等价。相等的概念是基于operator == 的。如果表达式"x ==y"返回真,则x和y的值相等,否则就不相等。等价是以“在已排序的区间中对象值的相对顺序”为基础的。考虑std::set s,如果两个Widget w1和w2,在s的排列顺序中哪个也不在另外一个的前面,那么,w1和w2对于s而言有等价的值。std:...原创 2019-10-25 13:11:27 · 5069 阅读 · 0 评论 -
18. 避免使用vector<bool>
一般情况下,对于容器std::vector,使用operator[],应该返回的是T& 或者const T&。而std::vector却不是,其返回的是一个代理对象。这里与常数相悖。若是需要bool的容器,可以使用std::deque或者使用bitset。...原创 2019-10-21 13:11:42 · 1372 阅读 · 0 评论 -
17. 使用“swap技巧”除去多余得容量
对于vector容器datas,std::vector<int> datas;...除去datas多余的容量,使用:std::vector<int>(datas).swap(datas);std::vector(datas)以datas作为参数构造一临时对象,这个临时对象中,size() = capactiy(),无任何多余容量。然后与datas进行swa...原创 2019-10-21 12:57:52 · 1511 阅读 · 0 评论 -
16.了解如何把vector和string数据传给旧的API
vector和string都是使用连续内存来存储数据,可以类似于普通数组进行处理。std::vector<int> datas;std::string strData;获取vector的首个元素的指针:&datas[0]&(*datas.begin())但必须确保容器非空,使用前最好调用empty()。C++11中,可以使用成员函数data()。...原创 2019-10-21 12:32:02 · 1218 阅读 · 0 评论 -
15. 注意string实现的多样性
几乎每个string的实现都包括如下信息:字符串的大小(size()),即它所包含的字符的个数。用于存储该字符串中字符的内存的容量(capacity())。字符串的值(value),即构成该字符串的字符。它的分配子的一份拷贝。这个字段是可选的。对值的引用计数。下面是常见的4中实现方式:不同的实现以不同的方式利用了这些涉及上的灵活性。总结这些区别如下:string的值可...原创 2019-10-20 11:45:37 · 1440 阅读 · 0 评论 -
14. 使用reserve避免不必要的内存分配
vector、string可以自动增长来容纳更多的元素。每当需要更多空间时,就调用与realloc类似的操作。realloc操作分为4个部分:分配一块大小为当前容量的某个倍数的新内存。在大多数实现中,vector和string的容量每次以2的倍数增长,即每当容器需要扩张是,它们的容量即加倍。把容器的所有元素从旧的内存中拷贝到新的内存中。析构掉内存中的对象。释放旧内存。涉及到内存...原创 2019-10-20 11:23:32 · 1899 阅读 · 0 评论 -
12. 切勿对STL容器的线程安全性有不切实际的依赖
STL自身对多线程的支持非常有限。对于STL,你能期望的是:多个线程读是安全的。多个线程对不同的容器做写入操作时安全的。在需要修改STL容器或这调用STL算法时需要自己加锁。为了实现异常安全,最好不要手动加锁解锁,多使用RAII。例如多使用std::lock_guard(),std::lock_guard是RAII模板类的简单实现,功能简单。std::lock_guard 在构造函数...原创 2019-10-19 17:41:32 · 1482 阅读 · 0 评论 -
11. 理解自定义分配子的合理用法
无原创 2019-10-19 17:20:32 · 1175 阅读 · 0 评论 -
10. 了解分配子(allocateor)的约定和限制
1 你的分配子是一个模板,模板参数T代表你为它分配内存的类型。2 提供类型定义pointer和reference,但是始终让pointer为T*,reference为T&。3 千万别让你的分配子拥有随对象而不同的状态(per-object state)。通常,分配子不应该有非静态的数据成员。4 记住,传给分配子的allocate成员函数的是那些要求内存的对象的个数,而不是所需要的字节...原创 2019-10-19 12:28:48 · 1384 阅读 · 0 评论