1.3.5 《Effective STL》
1.3.5 《Effective STL》
vimer-hz
书是越读越薄的
展开
-
[7 使用STL] 48. 总是include正确的头文件
为了帮助你记住什么时候需要包含哪些头文件,下面总结了每个与STL有关的标准头文件中所包含的内容:1 几乎所有的标准STL容器都被声明在与之同名的头文件中,如vector声明在<vector>中,list声明在<list>中。<set>和<map>是个例外,<set>包含set和multiset,<map>包含map和multimap。2 大部分算法声明在<algorithm>中,除了4个算法(accumulate,i转载 2021-09-07 23:29:54 · 150 阅读 · 0 评论 -
[7 使用STL] 47. 避免产生直写型(write-only)的代码
假定有一个vector<int>,现在要删除其中所有<x的元素,但是保留最后>=y的元素之前的所有元素。{1,1,3,...,7,8,1,2} x=3,y=8 则需要删除最后的1和2元素解决该问题的思路大致如下:1 通过以reverse_iterator作为参数调用find_if,找到容器中最后一个其值>=y的元素2 通过erase_remove习惯用法杀出区间中符合条件的元素看下面的代码:vector<int> v;int x,y;.转载 2021-09-07 23:18:30 · 171 阅读 · 0 评论 -
[7 使用STL] 46. 考虑使用函数对象而不是函数作为STL算法的参数
1 将函数对象传递给STL算法往往比传递实际的函数更高效假定需要将一个包含double类型数据的vector按降序排序,使用函数对象:vector<double> v;...sort(v.begin(), v.end(), greater<double>());使用内联函数:inline bool doubleGreater(double d1, double d2){ return d1 > d2;}...sort(v.begin(),转载 2021-09-06 23:56:03 · 150 阅读 · 0 评论 -
[7 使用STL] 45. 正确区分count、find、binary_search、lower_bound、upper_bound和equal_range
1 找到区间中某个值1.1 区间未排序想知道list容器中是否存在某个特定的Widget对象w。使用count:list<Widget> lw;Widget w;...if (count(lw.begin(), lw.end(), w) != 0) { // w在lw中 ...} else { // w不在lw中 ...}count返回0表示没找到,>0表示找到了元素。使用find:if (find(lw.begin()转载 2021-09-05 22:57:23 · 267 阅读 · 0 评论 -
[7 使用STL] 44. 容器的成员函数优先于同名的算法
有些STL容器提供了一些与算法同名的成员函数。如,关联容器提供了count、find、lower_bound、upper_bound和equal_range,而list则提供了remove、remove_if、unique、sort、merge和reverse。大多数情况下,你应该使用这些成员函数,而不是相应的STL算法。理由有两点:(1) 成员函数往往速度快(2) 成员函数通常与容器结合得更紧密,这是算法所不能比的1 关联容器假设有一个set<int>容器,其中包含了一百万转载 2021-09-02 23:13:46 · 102 阅读 · 0 评论 -
[7 使用STL] 43. 算法调用优先于手写的循环
STL是由容器、迭代器、算法以及函数对象组成的。本章主要讨论编程时什么时候使用循环、什么时候使用算法、什么时候使用容器的成员函数。由于STL算法涉及很广,所以意味着本该写循环完成的任务也可以用STL算法完成。假如有一个支持重画的Widget类:class Widget {public: ... void redraw() const; ...};当你重画一个list中的所有Widget对象时,可以用一个循环来完成:list<Widget> lw;转载 2021-08-31 22:24:45 · 79 阅读 · 0 评论 -
[6 函数子类及函数] 42. 确保less<T>与operator<具有相同的语义(POLA)
假设Widget包含一个重量值和一个最大速度值:class Widget {public: ... size_t weight() const; size_t maxSpeed() const; ...};通常情况下,按重量对Widget排序是最自然的方式:bool operator<(const Widget& lhs, const Widget& rhs){ return lhs.weight() < rhs.we转载 2021-08-28 11:04:05 · 113 阅读 · 0 评论 -
[6 函数子类及函数] 41. 理解ptr_fun、mem_fun和mem_fun_ref的来由
这些函数的主要任务是解决C++语法中的一个语法不一致问题。如果有一个函数f和一个对象x,希望在对象x上调用函数f,为了执行这个调用,C++提供了3种不同的语法:// 语法#1:f是一个非成员函数f(x);// 语法#2:f是一个成员函数,且x是一个对象或对象的引用x.f();// 语法#3:f是成员函数,且p是一个指向对象x的指针p->f();假如有一个测试函数test:// 测试函数testvoid test(Widget& w);vector<Wi转载 2021-08-27 15:25:59 · 214 阅读 · 0 评论 -
[6 函数子类及函数] 40. 若一个类是函数子类,则应使它可配接
假设有一个包含Widget对象指针的list容器:转载 2021-08-26 11:51:54 · 68 阅读 · 0 评论 -
[6 函数子类及函数] 39. 确保Predicate判别式是纯函数
先引入几个概念:一个判别式(predicate)是一个返回值为bool类型(或可以隐式转换为bool)的函数。一个纯函数(pure function)是指返回值仅仅依赖于其参数的函数。C++中,纯函数所能访问的数据应该仅局限于参数以及变量。判别式类(predicate class)是一个函数子类,它的operator()函数是一个判别式,STL中凡是能接受判别式的地方,也可以接受一个判别式类的对象。我们先看看违反此约束的后果,考虑以下设计拙劣的判别式类,它在第3次被调用的时候返回true,其转载 2021-08-20 12:53:46 · 132 阅读 · 0 评论 -
[6 函数子类及函数] 38. 遵循按值传递的原则来设计函数子类(桥接模式)
C或C++都不允许将函数作为参数传递,只能传递函数指针,以标准库中的qsort为例:void qsort(void* base, size_t nmemb, size_t size, int(*cmpfcn)(const void*, const void*));第46条会解释通常情况下sort算法优于qsort函数。cmpfcn这个函数指针采用了按值传递的方式。STL中函数对象在函数之间的传递也是按值传递的。以for_each算法为例,它需要一个函数对象作为参数,同时其转载 2021-08-19 16:13:27 · 114 阅读 · 0 评论 -
[5 算法] 37. 使用accumulate进行区间统计
对于区间,STL的算法:count:统计出区间中有多少元素count_if:统计出满足某个判别式的元素个数min_element:最小值max_element:最大值假如你需要按照自定义的方式对区间进行统计处理,用accumulate,头文件<numeric>。accumulate有两种形式:(1)参数是两个迭代器和一个初始值,返回该初始值加上由迭代器标识的区间中的值的总和list<double> ld;...double sum = acc转载 2021-08-18 12:42:47 · 233 阅读 · 0 评论 -
[5 算法] 36. 理解copy_if算法的正确实现
STL中有11个包含"copy"的算法:copy copy_backward repalce_copy reverse_copy replace_copy_if ...但是copy_if算法并没有。假如有一个函数来判断一个Widget是否有破损:bool isDefective(const Widget& w);你打算把一个vector中的所有破损Widget对象写到cerr中。下面是大多数人以为正确的copy_if实现和调用:templace<typename转载 2021-08-16 20:12:48 · 148 阅读 · 0 评论 -
[5 算法] 35. 通过mismatch或lexicographical_compare实现忽略大小写的字符串比较
首先,需要一种办法来判断两个字符是否相同,而不去管它们的大小写:1mismatchciStringCompare根据两个字符串之间的关系返回一个负数、零或正数。mismatch将标识出两个区间中第一个对应值不相同的位置。前提是,短的字符串作为第一个区间参数。mismatch返回一对迭代器,指示了这两个区间中对应字符第一次比较失败的位置:int ciStringCompareImpl(const string& s1, const string& s2);int ciS.转载 2021-08-16 18:44:46 · 141 阅读 · 0 评论 -
[5 算法] 34. 要求使用排序的区间作为参数的算法
有些算法要求使用排序的区间;有些算法使用排序的区间,效率会更高。要求使用排序区间的STL算法如下:1 binary_search,lower_bound,upper_bound,equal_range2 set_union,set_intersection,set_difference,set_symmetric_difference3 merge,inplace_merge4 includes另外,如下算法不要求排序的区间,但通常与排序的区间一起使用:5 unique,uniq转载 2021-08-13 15:44:45 · 90 阅读 · 0 评论 -
[5 算法] 33. 对包含指针的容器使用remove这一类算法时要特别小心
假如现在有一些动态分配的Widget,其中每一个Widget可能已经被验证过了,然后把结果指针存放在一个vector中:class Widget {public: // 是否被验证过 bool isCertified() const; ...};vector<Widget*> v;...v.push_back(new Widget);你想要删除那些没被验证过的Widget,基于第32条,你会自然地使用erase-remove_if习惯用法。//转载 2021-08-11 17:07:56 · 114 阅读 · 0 评论 -
[5 算法] 32. 如果确实需要删除元素,需要在remove这一类算法之后调用erase
下面是remove的声明:template<class ForwardIterator, class T>ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T& value);remove需要一对迭代器来指定所要进行操作的元素区间,它不接受容器作为参数,所以remove并不知道这些元素被存放在哪个容器中。从容器中删除元素的唯一方法是条用该容器的成员函数erase,而erase转载 2021-08-11 15:18:59 · 113 阅读 · 0 评论 -
[5 算法] 31. 了解各种排序选择(partition,stable_partition,nth_element,partial_sort,sort,stable_sort)
1 完全排序sort排序首先想到的是sort,但它并非在任何场合都是完美的,因为有时你不需要一个完全的排序。2 部分排序partial_sort如果你有一个存放Widget的vector,你需要将质量最好的20个Widget送给最重要的客户,那么只需排序出前20个Widget,其他的Widget可以不排序。bool qualityCompare(const Widget& lhs, const Widget& rhs){ // 返回lhs的质量是否好于rhs的质量转载 2021-08-10 16:16:02 · 231 阅读 · 0 评论 -
[5 算法] 30. 确保目标区间足够大(transform+inserter)
事实上,STL只有8个容器类,却包含超过100个算法。当新对象被添加进容器时,STL容器会自动扩容。但是,当程序员未能选用正确的方法来添加元素时,问题就来了。1 假如我们想把value中的全部元素以新元素的形式插入到results末尾// 根据x生成一个新的值int transmogrify(int x);vector<int> values;// values中存入一些值...vector<int> results;// 将transmogrify作用在v转载 2021-08-09 16:17:36 · 200 阅读 · 0 评论 -
[4 迭代器] 29. 对于逐个字符的输入请考虑使用istreambuf_iterator
假如我们想把一个文本文件的内容复制到一个string对象中:ifstream inputFile("xxx.txt");// 不正确string fileData(istream_iterator<char>(inputFile), istream_iterator<char>());这段代码并没有把文件中的空白字符复制到string对象中。因为istream_iterator使用operator>>函数来完成实际的读操作,而默认情况下operator>转载 2021-08-06 11:52:52 · 284 阅读 · 0 评论 -
[4 迭代器] 28. 正确理解由reverse_iterator的base()成员函数所产生的iterator的用法
先来看下面这段代码,它把数值1到5放进一个vector中,然后将一个reverse_iterator指向数值3,并且通过其base()函数初始化一个iterator:vector<int> v;v.reserve(5);// 插入1到5for (int i = 1;i <= 5; ++i) { v.push_back(i);}// 使ri指向3vector<int>::reverse_iterator ri = find(v.rbegin(), v.r转载 2021-08-06 10:13:11 · 561 阅读 · 0 评论 -
[4 迭代器] 27. 使用distance和advance将容器的const_iterator转换为iterator
你可能在想使用强制类型转换,让我们看看强制类型转换如何,下面试图将const_iterator转换为iterator:typedef deque<int> IntDeque;typedef IntDeque::iterator Iter;typedef IntDeque::const_iterator ConstIter;ConstIter ci;...// 编译错误,从const_iterator到iterator没有隐式转换Iter i(ci);// 编译错误,不能将c转载 2021-08-05 14:36:01 · 194 阅读 · 0 评论 -
[4 迭代器] 26. iterator优先于const_iterator、reverse_iterator以及const_reverse_iterator
STL标准容器提供了4种不同的迭代器:iterator、const_iterator、reverse_iterator和const_reverse_iterator。对容器类container<T>来说,iterator类型相当于T*,而const_iterator相当于const T*。reverse_iterator与const_reverse_iterator同样对应于T*和const T*,对这两个迭代器进行递增的效果是由容器尾部反向遍历到容器头部。先看两点说明。1 vect转载 2021-08-05 09:17:31 · 323 阅读 · 0 评论 -
[3 关联容器] 24. 当效率重要时,请在map::operator[]与map::insert之间做出正确选择
假定我们有一个Widget类,它支持默认构造函数,并根据一个double值来构造和赋值:class Widget {public: Widget(); Widget(double weight); Widget& operator=(double weight); ...};// 初始化map<int, Widght> m;m[1] = 1.50;map::operator[]的设计目的是为了提供添加和更新的功能。即对于map<转载 2021-08-03 17:36:04 · 161 阅读 · 0 评论 -
[3 关联容器] 21. 总是让比较函数在等值情况下返回false
来看一个有趣的现象。创建一个set,用less_equal作为它的比较类型,然后把10插入到该集合中,再插入10:// s用"<="来排序set<int, less_equal<int>> s;// 插入10s.insert(10);// 再插入10s.insert(10);less_equal意味着operator<=:// 检查10A和10B的等价性!(10A <= 10B) && !(10B <= 10A)转载 2021-08-03 09:48:50 · 138 阅读 · 0 评论 -
[3 关联容器] 20. 为包含指针的关联容器指定比较类型
假定有一个包含string*的set,打印集合内容,期望看到这些字符串是以字母顺序出现的:set<string*> ssp;ssp.insert(new string("Anteater"));ssp.insert(new string("Wombat"));ssp.insert(new string("Lenur"));ssp.insert(new string("Penguin"));for (set<string*>::const_iterator i = s转载 2021-08-02 16:25:08 · 84 阅读 · 0 评论 -
[3 关联容器] 19. 理解相等(equality)和等价(equivalence)的区别
STL容器有很多函数需要确定两个值是否相同,这些函数是以不同方式来判断两个值是否相同。find对相同的定义是相等,是以operator==为基础的。set::insert对相同的定义是等价,是以operator<为基础的。相等的概念是基于opetator==的,但应该记住,x和y有相等的值不一定意味着它们的所有数据成员都有相等的值。如下举例:class Widget {public: ...private: TimeStamp last; ...};转载 2021-08-02 11:05:58 · 408 阅读 · 0 评论 -
[2 vector和string] 18. 避免使用vector<bool>
vector<bool>不是STL容器,它并不存储bool。vector<bool> v;bool *pb = &v[0];上面代码不能编译,vecor<bool>是一个假的容器,存储的不是bool,而是一个二进制位。原因再介绍下,vector<bool>::operator[]返回一个对象,这个对象指向单个位的引用,即所谓的代理对象。vector<bool>看起来像这样:template<typename Al转载 2021-07-31 17:35:45 · 129 阅读 · 0 评论 -
[2 vector和string] 17. 使用swap除去多余的容量
我们需要一种方法能把容器的容量从之前的最大值缩减为当前需要的数量,这种对容量的缩减被称为"shrink to fit"。先看代码实现:vector<int> vi;...// 从vi中除去多余的容量vector<int>(vi).swap(vi);表达式vector<int>(vi)创建一个临时变量,它是vi的副本,是由vector的复制构造函数来完成的。注意,vector的复制构造函数只为所复制的元素分配需要的内存,所以临时变量没有多余的变量。转载 2021-07-31 15:13:36 · 176 阅读 · 0 评论 -
[2 vector和string] 16. 把vector和string数据传给旧的API
对于vector v,你需要得到一个指向v中数据的指针,从而可以吧v中的数据作为数组来对待,只需要&v[0]即可;对于string s,对应形式是s.c_str()。1 STL容器元素初始化C API(1)vector容器vector<int> v = {1, 2, 3};void doSomething(const int* pInts, size_t numInts) { for (int i = 0;i < numInts; i++) {转载 2021-07-31 14:46:39 · 120 阅读 · 0 评论 -
[2 vector和string] 15. 注意string实现的多样性
sizeof(string)的返回值是多少?如果你很关心内存的使用情况,这会是一个很重要的问题。我们需要知道一个string对象可能会存储哪些数据,以及它可能会把这些数据存储在什么地方。几乎每个string的实现都包含如下信息:(1) 字符串的大小size,即包含的字符数。(2) 用于存储字符串中字符内存的容量capacity。(3) 字符串的值value,即字符串的字符。除此之外,string还可能包含:(1) 分配子(2) 对值的引用计数这里将展示4种不同的str转载 2021-07-31 11:39:33 · 125 阅读 · 0 评论 -
[2 vector和string] 14. 使用reserve来避免不必要的重新分配
对于vector和string,每当需要更多空间时,调用与realloc类似的操作:1 分配一块大小为当前容量的某个倍数的新内存。2 把容器的所有元素从旧的内存复制到新的内存中。3 析构掉旧内存中的对象。4 释放旧内存。每当这些步骤发生时,vector或string中的所有指针、迭代器和引用都将变得无效。reserve函数能使你把重新分配的次数减少到最低,从而避免了指针、迭代器、引用失效带来的开销。todo...转载 2021-07-29 14:56:20 · 104 阅读 · 0 评论 -
[2 vector和string] 13. vector和string优先于动态分配的数组
当你决定用new来动态分配内存时,注意以下几点:1转载 2021-07-28 12:00:40 · 99 阅读 · 0 评论 -
[1 容器] 11. 理解自定义分配子的合理用法
allocator<T>是STL默认的内存管理器。用法举两个例子,如下:1 在一块共享内存中存放一个或多个容器,使得多个进程可以共享这些容器创建共享内存的堆接口:void* mallocShared(size_t bytesNeeded);void* freeShared(void* ptr);我们想把STL容器的内容放到这块共享内存中。template<typename T>class SharedMemoryAllocator {public:转载 2021-07-27 16:57:14 · 163 阅读 · 0 评论 -
[1 容器] 9. 慎重选择删除元素的方法
1 删除容器中的指定元素假定STL容器c,需要删除c中所有值为1963的元素。Container<int> c;注意,完成这一任务的方法随容器类型而异。(1) 连续内存容器vector,deque或string使用erase-remove习惯用法:// remove移动指定区间中的元素知道所有"不删除"的元素在区间的开头c.erase(remove(c.begin(), c.end(), 1963), c.end());(2) listc.remove(1转载 2021-07-27 09:47:02 · 130 阅读 · 0 评论 -
[1 容器] 7. 如果容器中包含了通过new创建的指针,切记析构前将指针delete掉
下面的代码会导致资源泄漏:void doSomething(){ vector<Widget*> vwp; for (int i=0; i<NUMBER; i++) { vwp.push_back(new Widget); } ...} //Widget泄漏你希望它们被删除:void doSomething(){ ... for (vector<Widget*>::iterator i = v转载 2021-07-26 15:06:44 · 87 阅读 · 0 评论 -
[1 容器] 5. 区间成员函数优先于与之对应的单元素成员函数
给定v1和v2两个vector,使v1的内容和v2的后半部分内容相同,最简单的操作是:v1.assign(v2.begin() + v2.size()/2, v2.end());上述例子两个目的:1 xxx转载 2021-07-26 10:44:52 · 116 阅读 · 0 评论 -
[1 容器] 4. 调用empty()而不是检查size()是否为0
应该使用empty()形式,理由很简单:empty()对所有的标准容器都是常数时间操作,而size()对于list耗费的是线性时间。介绍list的拼接操作splice:list<int> list1;list<int> list2;...// 把list2中从第一个含5的节点到最后一个含10的所有节点移动到list1的末尾// base()见第28条list1.splice(list1.end(), list2, find(list2.begin()转载 2021-07-23 16:58:01 · 107 阅读 · 0 评论 -
[1 容器] 3. 确保容器中的对象副本正确且高效
当向容器加入对象时(pushback等),存入容器的是你所指定对象的副本。当从容器中取出一个对象时(front等),你所得到的是容器中所保存对象的副本。copy in。copy out。这就是STL的工作方式。这种复制是利用对象的复制成员函数实现的。如下:class Widget{public: ... // 复制构造函数 Widget(const Widget&); // 复制赋值操作符 Widget& operator=(const Wid转载 2021-07-23 16:31:46 · 131 阅读 · 0 评论 -
[1 容器] 1. 慎重选择容器类型
本章讲述适用于STL容器的准则,随后几章就特定类型的容器展开论述。注意:auto_ptr已经弃用,可用shared_ptr或unique_ptr来替换。1. 慎重选择容器类型C++容器分类如下:1 标准STL序列容器vector,string,deque和list。频繁在序列中间插入和删除操作,应使用list。频繁在序列头部,尾部插入和删除操作,应使用deque。2 标准STL关联容器set,multiset,map和multimap。3 非标准序列容器sli转载 2021-07-23 15:38:38 · 235 阅读 · 0 评论