NOTES of "effective STL"
来源:互联网 发布:centos输入法下载安装 编辑:程序博客网 时间:2024/06/05 14:18
/**************************
* NOTES of "Effective STL"
* Written on May 13rd, 2016
* Author: Dennis LU
* ************************/
#1 容器
1. 慎重选择容器类型
- 序列容器:vector / deque / list / string
- 关联容器:set / map / multiset / multimap
- 非标准关联容器: hash_set / hash_map
- 其他:stack / queue / heap / priority_queue
- queue & stack 实现如果用vector,是无法事先确认其长度的,所以增加删除都不高效,只能用deque
- priority_queue 实现用heap作为其底层数据结构(top值一直是min/max),当然也可以用排序的vector来做
- vector 优势:内存连续,可random visit,容器的构建sizeof不大
劣势:insert/erase 不高效,内存可能需要重新分配和copy,push_back/pop_back 同样如此
iterator容易失效,需要reserve空间
- deque 优势: 内存是一段段连续的,push_back/push_front等操作高效
劣势:容器构建的sizeof较大
- list 优势:insert/erase 高效,因为无法用algorithm的API所以有自己的remove/sort/merge/unique
劣势:无法random visit, 遍历需要O(N)
- hash VS 关联容器: HASH是精确打击,能很快找到某个元素 / 关联容器更容易区域打击,找到一片
- heap:
make_heap(pvec->begin(),pvec->end(),greater<int>());
pvec->push_back(111);
push_heap(pvec->begin(),pvec->end());
pop_heap(pvec->begin(),pvec->end());
pvec->pop_back();
sort_heap(pvec->begin(), pvec->end());
3. 确保容器中的对象拷贝正确且高效
- 如果说容器中是Widget对象,经常拷贝肯定开销较大,那就在容器中存放Widget*, 指针拷贝快
- 如果放指针,一定要注意,在容器销毁时,不会销毁指针的malloc空间造成MemoryLeak -> 智能指针
4. 调用empty 而不是size()==0
- 在大部分容器中,size() 不会是O(N)操作,但是在list中,因为有splice操作,size不一定是常数操作
- list.splice(list.end(), list2, find(list2.begin(),list2.end(),5), find(...,10))
- 这时,我们不知道两个find之间是几个元素,方法1,我们遍历一遍更新size,方法2,不遍历
- 因为list的实现需要splice常数时间,所以size就不是常数时间了
5. 成员函数效率优于通用函数
- vector.assign() VS copy(vec.begin(),vec.size()/2, back_inserter(vec2))
- 正确的写vector的批量输入:
for(i=0;i<NUM;i++){
loc = vec.insert(loc,value);
loc++;
}//这种写法就算capacity没问题,每次insert都要移动数据,也是不高效的
//一个个的insert进来,不如一下子insert进来所有数据
//不如这种:vec.insert(loc, iter1, iter2)
7. 如果容器中有new出来的指针,不要忘记delete掉
vector<Widget*> wv;
for(){
wv.push_back(new Widget);
}
wv会自动删掉,但是里面的Widget*不会,他们造成了MemoryLeak
你需要:
struct DeleteObject{
template<typename T>
void operator()(const T* ptr) const{
delete ptr;
}
}
//for_each,优于for loop, 不怕中间的异常跳出,破坏loop
for_each(wv.begin(),wv.end(), DeleteObject());
/* 最好的解决方法是用SmartPointer */
typedef boost::shared_ptr<Widget> WSP;
vector<WSP> vec;
for(){
vec.push_back(WSP(new Widget));
}
//shapred_ptr<Widget> ptr(new Widget);
9. 正确的删除容器内元素
- 首先,用容器自己的api(erase)最有效
- 需要用remove/remove_if,都别忘记erase-remove结构
vec.erase(remove(vec.begin(),vec.end(),99),vec.end())
//remove 不删除元素,只是将符合条件的元素移到最后面
- list用自己的API:remove, 因为list不能用通用的remove(可能是list无法RandomVisit)
- 关联容器 用 erase
- 需要区域的删除元素:
- vector/deque/string
for(){
if(badValue(*iter))
iter = vec.erase(iter);
else
iter++;
}
- set/map
for(){
if(badValue(*iter))
set.erase(iter++);
else
iter++;
}
k
12. STL不完全能够做到线程安全
- 最好还是上锁安全点
- RAII(Resource Acquisition is initialization), 用RAII来创建LOCK
/******************
#2 vector && String
*******************/
13. vector & string 优先动态分配的数组
- new出来的不要忘记delete,选择正确的delete或者delete[],不要重复delete
- 用vector等方便内存管理
14. 用reserve避免不必要的内存分配
- 内存分配是按照2-4-8这样或者是原先size的double再double,反应在capacity
- 涉及内存重新分配,开销很大;有重新new一块空间,拷贝原先数据,iterator失效
15. 注意string实现的多样性
- 这节是说string的各个平台的实现,导致string的sizeof不一样
- sizeof(string) == 8
- sizeof(vector) == 24
- sizeof(deque) == 80
- sizeof(list) == 16
- sizeof(set/map)== 48
- 以上基于64位,sizeof(char*) == 8
16. 如何把vector和string传递给旧的API
- void foo(int* array) ----- &vector[0]
- void foo(char* ) ---------- string.c_str()
17. 使用swap技巧除去多余的容量
- vector<int>(iv).swap(iv) ------ 去掉的是capacity-size的空间
- vector<int>().swap(iv) ------ 类似clear
18. 避免使用vector<bool>
- 一个bool只占一个二进制位,一个字节可以容纳8个bool
- 不是真正的容器
- 可以使用deque<bool> 或者 bitset
/************
#3 关联容器
*************/
19. 相等和等价的区别
- 相等的概念是基于 operator==
- 等价的概念是基于 operator<, 等价的关系是以“已排序区间中的对象值的相对顺序”为基础
- 等价是如:Widget w1 & w2, w1和w2哪个都不在对方之前,即视为等价
- set<Widget>的默认比较函数是less<Widget>, 在less<Widget>里是看operator<
- 等价:!(w1 < w2) && !(w2 < w1)
- 关联容器强调排序,pred(默认less)由用户自己定义,成功的判断返回false
-
20. 为包含指针的关联容器指定比较类型
- 关联容器里放指针,首先是smartPointer比较好,其次,不会对指针自动排序,要指定比较函数
- set<string*, less<string*> > stringSet;
- 或者这样
struct StringPtrLess :
public binary_function<const string*,
const string*,
bool> {
bool operator() (const string* ps1, const string* ps2) const {
return *ps1 < *ps2;
}
};
//加入template
struct StringPtrLess {
template<typename T>
bool operator() (T pt1, T pt2) const{
return *pt1 < *pt2;
}
};
typedef set<string*, StringPtrLess> StringSet;
StringSet ssp;
//然后可以遍历
for(StringSet::const_iterator it=ssp.begin(); i!=ssp.end();i++)
cout<<**i<<endl;
//说过用for loop不是个good idea,选择用for_each
void print(const string* sp){
cout<< *sp <<endl;
}
for_each(ssp.begin(),ssp.end(),print);
//这样写出来的print不够好,再改下
struct PRINT{
template<typename T>
void operator() (const T* t) const{
cout<< *t <<endl;
}
};
for_each(ssp.begin(), ssp.end(), PRINT());
//最后需要注意的是:
//你可能会想给set添加个bool比较函数就行,其实这样编译不过
bool StringLess (const string* s1, const string* s2) const{
return *s1 < *s2;
}//这种比较函数在set<string*, StringLess >会编译不过
21. 总是让比较函数在等值的情况下返回False
- 在关联容器做比较函数时,不要用小于等于 或 大于等于,就只用小于 或者 大于。
22. 不要直接修改关联容器的键
- 关联容器是按照RBTree来排序的,直接改变其中的键,对排序就乱了
- map的设计者已经把键设计成const,所以修改键,会引起报错
- 但是set的设计者并没有设计成const,因为set<Person_Info>,若Person_Info类中的比较类型是
以Person的age大小,如果设计成const,在后面的代码中将无法修改Person_Info类中的其他信息
同样的map不存在这样的问题。所以set无法使用const键
- 说道const,我们梳理下const概念:
1. const int a = 100; 这个可以写在全局只读数据段,申明的是全局int a; 也可以是在函数类,是
写在堆栈里面。
2. 在全局的const的修改,可以用 const_cast<int*>, 编译没问题,运行就段错误
3. 所以只能修改在堆栈里的const变量:
const int *a = new int(10);
int* p = const_cast<int*>(a);
const int a = 100;
int* p = const_cast<int*>(&a);
这两种方法,a和*p都被修改
const int a =100;
int* p = (int*)(&a);
*p修改了,但是cout<<a 时,a还是原值
23. 考虑用排序的vector来代替关联容器
- 关联容器的优势是insert和erase速度快,RBTree结构的查找
- 如果数据量一下子全了,然后操作只有查找,用排序后的vector就比关联容器更好
- 排序后的vector,用binary_search/equal_range/lower_bound/upper_bound,都可以
- 最后书中说,关联容器的内存空间不连续,相对连续的vector,更造成页面置换,影响效率
- 类似代码如:
vector<Widget> vw;
sort(vw.begin(),vw.end(),pred);
Widget w;
if(binary_search(vw.begin(),vw.end(),w)){
pair<vector<Widget>::iterator, vector<Widget>::iterator> ret =
equal_range(vw.begin(), vw.end(),w);
if(ret.first != ret.second){
//found
//这个地方又牵涉到 ret.second - ret.first 能不能用的问题
//如果是连续内存,如vector,可以用ret.second-ret.first来确定有几个
//那如果是list这种不连续内存,只能用遍历的方法得到
//for(list<int>::iterator it=ret.first; it!=ret.end();it++){
// num++;
//}
//或者distance(list.begin(),list.end())
}
}
24. 在效率至关重要时,分辨map::operator[] 和 map::insert的区别
- map::operator[] 在调用后,先去找是否存在:Key存在,改变value、
若key不存在,则调用map::insert来创建Key::value
- map::insert 在键值不存在的时候,创建它
25. 熟悉非标准的散列容器
- hash_set
- hash_map
/*******************
#4 迭代器 Iterator
********************/
26. iterator 优先于 const_iterator & reverse_iterator & const_reverse_iterator
- insert/erase 入参不能使用除iterator外的迭代器
- iterator = reverse_iterator.base() //NOT the same element point
- const_iterator = const_reverse_iterator.base()
- iterator = advance(i, distance<vector<int>::const_iterator>(i,ci));
- 尽量用iterator,将const转化过来需要时间:连续的要O(1),list等需要O(n)
27. const_iterator 转化成 iterator (使用advance & distance)
- demo代码:
typedef vector<int>::iterator Iter;
typedef vector<int>::const_iterator ConstIter;
vector<int> iv;
ConstIter ci;
...
Iter i(iv.begin());
advance(i, distance<ConstIter>(i,ci));
- 解释:先将iter定义在begin()上,然后通过计算ci和begin()的距离,再由advance累加
28. 正确理解由reverse_iterator的base()所产生的iterator
- 由find(vector.rbegin(),vector.rend(),value) 返回的必须是reverse_iterator
- vector<int>::reverse_iterator re_iter = find(iv.rbegin(),iv.rend(),value)
- 但是这个reverse_iterator 不能用于insert和erase: iv.erase(re_iter)//NOT work
- 必须是iterator才行,所以,reverse_iterator 得转变成iterator
- iterator = reverse_iterator.base()
- 问题来了:iterator 不是指向reverse_iterator的值
- vector<int> = {1,2,3,4,5}, find(rbegin,rend,3), riter -> 3
- iter(riter.base()) -> 3后面的4
/****************
#5 算法
****************/
30. 确保目标区间足够大
- demo代码:
int add5(int x);
vector<int> values, results;
transform(values.begin(),values.end(),
back_inserter(results),add5);
- back_inserter() 调用push_back。若front_inserter调用push_front,确保容器支持
- 这样的元素拷贝对string、vector等顺序容器不理想,他们需要reserve且最好保证一次性
全部拷贝进去,transform这样的一个个元素insert进来的,需要频繁移动数据。
31. 了解各种排序
- 序列容器:nth_element > partial_sort > sort > stable_sort
- list不可以random visit,所以不能用上述api,list有成员函数sort
- 关联容器自身就是有序的
- partition/stable_partition 根据某个条件分段,速度快
- 根据某个条件,但具体不知道几个 ------ partition
- 知道具体几个,如前75% ------ nth_element
- 前75%也是要排序的 ------ partial_sort
- 全排序 ----—- sort
- 等值的不改变前后顺序 ------ stable_sort
32. vector.erase(remove(),end())
- remove 操作是将符合条件的移到最后,并没有实际删除掉
- 用erase+remove操作才能真正删除
33. remove包含指针的容器
- 因为容器的放的是指针,而指针指向的空间并没有delete,可能会造成内存泄漏
- 除了记得删掉指针的内存,也可以用智能指针来代替
34. 需要先排好序的api
- binary_search/equal_range/lower_bound/upper_bound
- unique: 只会寻找前一个后一个是否相同.ababab就发现不了
- 如果sort是按照greater<int>来排序,binary_search也得加上,因为
binary_search是按照<来默认排序的
binary_search(iv.begin(),iv.end(),value, greater<int>())
/****************
#7 在程序中使用STL
*****************/
43. 算法调用优于手写的循环
- for_each比for要好,能捕捉异常并释放资源
-
44. 容器的成员函数优于同名算法函数
- set.find VS. find()
45. 区分count/find/binary_search/lower_bound/upper_bound/equal_range
- find 存在性测试, 无序空间
- 排序过的,对数时间的bianry_search,它只回答在不在,返回bool值
- lower_bound:返回value值在的地方或value不在,他应该被插入的地方
- lower_bound返回的iterator,不要直接iter!=end() && *iter == value
- lower_bound更适合用于找到某个条件的左值,在一个位置上进行删除
- iv.erase(lower_bound(),iv.end)
- 推荐使用equal_range, distance(p.first,p.second)确定几个元素
46. 使用函数对象 而不是函数 作为STL算法的参数
- 自己写的函数,即使申明inline,最后编译的时候可能没有inline,所以效率
上没有函数对象好
* NOTES of "Effective STL"
* Written on May 13rd, 2016
* Author: Dennis LU
* ************************/
#1 容器
1. 慎重选择容器类型
- 序列容器:vector / deque / list / string
- 关联容器:set / map / multiset / multimap
- 非标准关联容器: hash_set / hash_map
- 其他:stack / queue / heap / priority_queue
- queue & stack 实现如果用vector,是无法事先确认其长度的,所以增加删除都不高效,只能用deque
- priority_queue 实现用heap作为其底层数据结构(top值一直是min/max),当然也可以用排序的vector来做
- vector 优势:内存连续,可random visit,容器的构建sizeof不大
劣势:insert/erase 不高效,内存可能需要重新分配和copy,push_back/pop_back 同样如此
iterator容易失效,需要reserve空间
- deque 优势: 内存是一段段连续的,push_back/push_front等操作高效
劣势:容器构建的sizeof较大
- list 优势:insert/erase 高效,因为无法用algorithm的API所以有自己的remove/sort/merge/unique
劣势:无法random visit, 遍历需要O(N)
- hash VS 关联容器: HASH是精确打击,能很快找到某个元素 / 关联容器更容易区域打击,找到一片
- heap:
make_heap(pvec->begin(),pvec->end(),greater<int>());
pvec->push_back(111);
push_heap(pvec->begin(),pvec->end());
pop_heap(pvec->begin(),pvec->end());
pvec->pop_back();
sort_heap(pvec->begin(), pvec->end());
3. 确保容器中的对象拷贝正确且高效
- 如果说容器中是Widget对象,经常拷贝肯定开销较大,那就在容器中存放Widget*, 指针拷贝快
- 如果放指针,一定要注意,在容器销毁时,不会销毁指针的malloc空间造成MemoryLeak -> 智能指针
4. 调用empty 而不是size()==0
- 在大部分容器中,size() 不会是O(N)操作,但是在list中,因为有splice操作,size不一定是常数操作
- list.splice(list.end(), list2, find(list2.begin(),list2.end(),5), find(...,10))
- 这时,我们不知道两个find之间是几个元素,方法1,我们遍历一遍更新size,方法2,不遍历
- 因为list的实现需要splice常数时间,所以size就不是常数时间了
5. 成员函数效率优于通用函数
- vector.assign() VS copy(vec.begin(),vec.size()/2, back_inserter(vec2))
- 正确的写vector的批量输入:
for(i=0;i<NUM;i++){
loc = vec.insert(loc,value);
loc++;
}//这种写法就算capacity没问题,每次insert都要移动数据,也是不高效的
//一个个的insert进来,不如一下子insert进来所有数据
//不如这种:vec.insert(loc, iter1, iter2)
7. 如果容器中有new出来的指针,不要忘记delete掉
vector<Widget*> wv;
for(){
wv.push_back(new Widget);
}
wv会自动删掉,但是里面的Widget*不会,他们造成了MemoryLeak
你需要:
struct DeleteObject{
template<typename T>
void operator()(const T* ptr) const{
delete ptr;
}
}
//for_each,优于for loop, 不怕中间的异常跳出,破坏loop
for_each(wv.begin(),wv.end(), DeleteObject());
/* 最好的解决方法是用SmartPointer */
typedef boost::shared_ptr<Widget> WSP;
vector<WSP> vec;
for(){
vec.push_back(WSP(new Widget));
}
//shapred_ptr<Widget> ptr(new Widget);
9. 正确的删除容器内元素
- 首先,用容器自己的api(erase)最有效
- 需要用remove/remove_if,都别忘记erase-remove结构
vec.erase(remove(vec.begin(),vec.end(),99),vec.end())
//remove 不删除元素,只是将符合条件的元素移到最后面
- list用自己的API:remove, 因为list不能用通用的remove(可能是list无法RandomVisit)
- 关联容器 用 erase
- 需要区域的删除元素:
- vector/deque/string
for(){
if(badValue(*iter))
iter = vec.erase(iter);
else
iter++;
}
- set/map
for(){
if(badValue(*iter))
set.erase(iter++);
else
iter++;
}
k
12. STL不完全能够做到线程安全
- 最好还是上锁安全点
- RAII(Resource Acquisition is initialization), 用RAII来创建LOCK
/******************
#2 vector && String
*******************/
13. vector & string 优先动态分配的数组
- new出来的不要忘记delete,选择正确的delete或者delete[],不要重复delete
- 用vector等方便内存管理
14. 用reserve避免不必要的内存分配
- 内存分配是按照2-4-8这样或者是原先size的double再double,反应在capacity
- 涉及内存重新分配,开销很大;有重新new一块空间,拷贝原先数据,iterator失效
15. 注意string实现的多样性
- 这节是说string的各个平台的实现,导致string的sizeof不一样
- sizeof(string) == 8
- sizeof(vector) == 24
- sizeof(deque) == 80
- sizeof(list) == 16
- sizeof(set/map)== 48
- 以上基于64位,sizeof(char*) == 8
16. 如何把vector和string传递给旧的API
- void foo(int* array) ----- &vector[0]
- void foo(char* ) ---------- string.c_str()
17. 使用swap技巧除去多余的容量
- vector<int>(iv).swap(iv) ------ 去掉的是capacity-size的空间
- vector<int>().swap(iv) ------ 类似clear
18. 避免使用vector<bool>
- 一个bool只占一个二进制位,一个字节可以容纳8个bool
- 不是真正的容器
- 可以使用deque<bool> 或者 bitset
/************
#3 关联容器
*************/
19. 相等和等价的区别
- 相等的概念是基于 operator==
- 等价的概念是基于 operator<, 等价的关系是以“已排序区间中的对象值的相对顺序”为基础
- 等价是如:Widget w1 & w2, w1和w2哪个都不在对方之前,即视为等价
- set<Widget>的默认比较函数是less<Widget>, 在less<Widget>里是看operator<
- 等价:!(w1 < w2) && !(w2 < w1)
- 关联容器强调排序,pred(默认less)由用户自己定义,成功的判断返回false
-
20. 为包含指针的关联容器指定比较类型
- 关联容器里放指针,首先是smartPointer比较好,其次,不会对指针自动排序,要指定比较函数
- set<string*, less<string*> > stringSet;
- 或者这样
struct StringPtrLess :
public binary_function<const string*,
const string*,
bool> {
bool operator() (const string* ps1, const string* ps2) const {
return *ps1 < *ps2;
}
};
//加入template
struct StringPtrLess {
template<typename T>
bool operator() (T pt1, T pt2) const{
return *pt1 < *pt2;
}
};
typedef set<string*, StringPtrLess> StringSet;
StringSet ssp;
//然后可以遍历
for(StringSet::const_iterator it=ssp.begin(); i!=ssp.end();i++)
cout<<**i<<endl;
//说过用for loop不是个good idea,选择用for_each
void print(const string* sp){
cout<< *sp <<endl;
}
for_each(ssp.begin(),ssp.end(),print);
//这样写出来的print不够好,再改下
struct PRINT{
template<typename T>
void operator() (const T* t) const{
cout<< *t <<endl;
}
};
for_each(ssp.begin(), ssp.end(), PRINT());
//最后需要注意的是:
//你可能会想给set添加个bool比较函数就行,其实这样编译不过
bool StringLess (const string* s1, const string* s2) const{
return *s1 < *s2;
}//这种比较函数在set<string*, StringLess >会编译不过
21. 总是让比较函数在等值的情况下返回False
- 在关联容器做比较函数时,不要用小于等于 或 大于等于,就只用小于 或者 大于。
22. 不要直接修改关联容器的键
- 关联容器是按照RBTree来排序的,直接改变其中的键,对排序就乱了
- map的设计者已经把键设计成const,所以修改键,会引起报错
- 但是set的设计者并没有设计成const,因为set<Person_Info>,若Person_Info类中的比较类型是
以Person的age大小,如果设计成const,在后面的代码中将无法修改Person_Info类中的其他信息
同样的map不存在这样的问题。所以set无法使用const键
- 说道const,我们梳理下const概念:
1. const int a = 100; 这个可以写在全局只读数据段,申明的是全局int a; 也可以是在函数类,是
写在堆栈里面。
2. 在全局的const的修改,可以用 const_cast<int*>, 编译没问题,运行就段错误
3. 所以只能修改在堆栈里的const变量:
const int *a = new int(10);
int* p = const_cast<int*>(a);
const int a = 100;
int* p = const_cast<int*>(&a);
这两种方法,a和*p都被修改
const int a =100;
int* p = (int*)(&a);
*p修改了,但是cout<<a 时,a还是原值
23. 考虑用排序的vector来代替关联容器
- 关联容器的优势是insert和erase速度快,RBTree结构的查找
- 如果数据量一下子全了,然后操作只有查找,用排序后的vector就比关联容器更好
- 排序后的vector,用binary_search/equal_range/lower_bound/upper_bound,都可以
- 最后书中说,关联容器的内存空间不连续,相对连续的vector,更造成页面置换,影响效率
- 类似代码如:
vector<Widget> vw;
sort(vw.begin(),vw.end(),pred);
Widget w;
if(binary_search(vw.begin(),vw.end(),w)){
pair<vector<Widget>::iterator, vector<Widget>::iterator> ret =
equal_range(vw.begin(), vw.end(),w);
if(ret.first != ret.second){
//found
//这个地方又牵涉到 ret.second - ret.first 能不能用的问题
//如果是连续内存,如vector,可以用ret.second-ret.first来确定有几个
//那如果是list这种不连续内存,只能用遍历的方法得到
//for(list<int>::iterator it=ret.first; it!=ret.end();it++){
// num++;
//}
//或者distance(list.begin(),list.end())
}
}
24. 在效率至关重要时,分辨map::operator[] 和 map::insert的区别
- map::operator[] 在调用后,先去找是否存在:Key存在,改变value、
若key不存在,则调用map::insert来创建Key::value
- map::insert 在键值不存在的时候,创建它
25. 熟悉非标准的散列容器
- hash_set
- hash_map
/*******************
#4 迭代器 Iterator
********************/
26. iterator 优先于 const_iterator & reverse_iterator & const_reverse_iterator
- insert/erase 入参不能使用除iterator外的迭代器
- iterator = reverse_iterator.base() //NOT the same element point
- const_iterator = const_reverse_iterator.base()
- iterator = advance(i, distance<vector<int>::const_iterator>(i,ci));
- 尽量用iterator,将const转化过来需要时间:连续的要O(1),list等需要O(n)
27. const_iterator 转化成 iterator (使用advance & distance)
- demo代码:
typedef vector<int>::iterator Iter;
typedef vector<int>::const_iterator ConstIter;
vector<int> iv;
ConstIter ci;
...
Iter i(iv.begin());
advance(i, distance<ConstIter>(i,ci));
- 解释:先将iter定义在begin()上,然后通过计算ci和begin()的距离,再由advance累加
28. 正确理解由reverse_iterator的base()所产生的iterator
- 由find(vector.rbegin(),vector.rend(),value) 返回的必须是reverse_iterator
- vector<int>::reverse_iterator re_iter = find(iv.rbegin(),iv.rend(),value)
- 但是这个reverse_iterator 不能用于insert和erase: iv.erase(re_iter)//NOT work
- 必须是iterator才行,所以,reverse_iterator 得转变成iterator
- iterator = reverse_iterator.base()
- 问题来了:iterator 不是指向reverse_iterator的值
- vector<int> = {1,2,3,4,5}, find(rbegin,rend,3), riter -> 3
- iter(riter.base()) -> 3后面的4
/****************
#5 算法
****************/
30. 确保目标区间足够大
- demo代码:
int add5(int x);
vector<int> values, results;
transform(values.begin(),values.end(),
back_inserter(results),add5);
- back_inserter() 调用push_back。若front_inserter调用push_front,确保容器支持
- 这样的元素拷贝对string、vector等顺序容器不理想,他们需要reserve且最好保证一次性
全部拷贝进去,transform这样的一个个元素insert进来的,需要频繁移动数据。
31. 了解各种排序
- 序列容器:nth_element > partial_sort > sort > stable_sort
- list不可以random visit,所以不能用上述api,list有成员函数sort
- 关联容器自身就是有序的
- partition/stable_partition 根据某个条件分段,速度快
- 根据某个条件,但具体不知道几个 ------ partition
- 知道具体几个,如前75% ------ nth_element
- 前75%也是要排序的 ------ partial_sort
- 全排序 ----—- sort
- 等值的不改变前后顺序 ------ stable_sort
32. vector.erase(remove(),end())
- remove 操作是将符合条件的移到最后,并没有实际删除掉
- 用erase+remove操作才能真正删除
33. remove包含指针的容器
- 因为容器的放的是指针,而指针指向的空间并没有delete,可能会造成内存泄漏
- 除了记得删掉指针的内存,也可以用智能指针来代替
34. 需要先排好序的api
- binary_search/equal_range/lower_bound/upper_bound
- unique: 只会寻找前一个后一个是否相同.ababab就发现不了
- 如果sort是按照greater<int>来排序,binary_search也得加上,因为
binary_search是按照<来默认排序的
binary_search(iv.begin(),iv.end(),value, greater<int>())
/****************
#7 在程序中使用STL
*****************/
43. 算法调用优于手写的循环
- for_each比for要好,能捕捉异常并释放资源
-
44. 容器的成员函数优于同名算法函数
- set.find VS. find()
45. 区分count/find/binary_search/lower_bound/upper_bound/equal_range
- find 存在性测试, 无序空间
- 排序过的,对数时间的bianry_search,它只回答在不在,返回bool值
- lower_bound:返回value值在的地方或value不在,他应该被插入的地方
- lower_bound返回的iterator,不要直接iter!=end() && *iter == value
- lower_bound更适合用于找到某个条件的左值,在一个位置上进行删除
- iv.erase(lower_bound(),iv.end)
- 推荐使用equal_range, distance(p.first,p.second)确定几个元素
46. 使用函数对象 而不是函数 作为STL算法的参数
- 自己写的函数,即使申明inline,最后编译的时候可能没有inline,所以效率
上没有函数对象好
0 0
- NOTES of "effective STL"
- notes of Effective C++
- Notes 1 of More Effective C++ —— Basics
- STL notes
- [NOTES] Effective C++
- Effective C++ Reading Notes
- Effective STL
- Effective STL
- Effective STL
- Effective stl
- Effective STL
- Effective STL
- Effective STL
- effective STL
- effective stl
- effective STL
- Effective STL
- C++ STL notes
- js_乱七八糟
- fflush(stdin)与fflush(stdout)
- 归纳一下:C#线程同步的几种方法 2
- 半深入理解Java属性继承
- c++第七次上机-特殊三位数
- NOTES of "effective STL"
- Xcode 7 制作静态库.a 文件
- 面试中常见链表问题6:划分链表
- 自定义iOS7导航栏背景,标题和返回按钮文字颜色
- C#多线程中WaitOne函数的?
- svn
- HTTP Live Streaming直播(iOS直播)技术分析与实现
- Promise 总结
- ScopeGuard : 安全清理资源