Effective STL
1: 慎重选择容器类型
2: 不要试图编写独立于容器类型的代码
3: 确保容器中的对象copy正确且搞笑
存在继承关系时的copy会导致分割, 可以存储对象指针.
4: 调用empty而不是检查size()是否为0
empty对所有标准容器是常数时间操作.
size()返回容器元素个数, 耗费线性时间.capacity()返回能够存储的总数, 对于vector(type) c, capacity为0, 不能赋值, 必须用reserve分配空间.
5: 区间成员函数优先于与之对应的单元素成员函数.
避免写显示循环, 这样减少对象拷贝, 减少代码.
1 vector<int> v1, v2; 2 3 //... 4 5 v1.clear(); 6 7 for(vector<int>::const_iterator ci = v2.begin(); ci != v2.end(); ++ci) 8 9 {10 11 v1.push_back(*ci);12 13 }14 15 相当于16 17 v1.assign(v2.begin(), v2.end());18 19
而算法
copy(v2.begin(), v2.end(), back_inserter(v1));
中尽管没有显示循环, 但是copy算法中可能有. 可以改为:
v1.insert(v1.end(), v2.begin(), v2.end());
6: C++编译器最烦人的分析机制
C++尽可能的解释为函数声明;
1 ifstream dataFile("xx.txt");2 list<int>data(istream_iterator<int>(dataFile),3 istream_iterator<int>()); //被认为是函数声明, 而不是够找一个对象.
最好改成:
1 ifstream dataFile("xx.txt");2 3 istream_iterator<int> data1(dataFile);4 5 istream_iterator<int> data2;6 7 list<int>data(data1, d
ata2);
7: 如果容器中包含了通脱new操作创建的指针, 在容器对象析够前将指针delete.
容器在析构时会析构容器元素, 但是如果是指针容器, 无delete操作, 需手动delete.
或者可以存储只能指针,
8: 切勿创建包含auto_ptr的容器.
iterator_traits<pointobj>::value_type为pointobj所指向的类型, 如果pointobj为vector<int>::iterator, 则iterator_traits<pointobj>::value_type为int
9: 慎重选择删除元素的方法
erase-remove方法.
set, multiset, map, multimup无remove方法.
erase后,所有删除元素之后的指向元素的迭代器会无效.
10. 了解分配自的约定和限定
11. 理解自定义分配子的合理用法
12. 切勿对STL容器的线程安全性有不切实际的依赖
13. vector和string优先于动态分配的数组
string使用了引用计数计数, 在多线程中会比较麻烦, 需要同步控制, 使用vector<char>替代string, vector不允许使用引用计数.
14. 使用reserve来避免不必要的重新分配
15: 注意string实现的多样性
16: 了解如何把vector和string数据传给旧的api
vector使用&v[0], 或者&*v.begin(), 注意,vector可能为空, 则产生野指针.
string使用s.c_str();
17: 使用swap技巧除去多余的容量.
vector<int> v;
//...
vector<int>(v).swap(v); // vector<int>(v)创建临时变量, 是v的copy, 而vector的copy构造函数只分配所需的内存. swap止呕, v去除了不需要的空间.
18: 避免使用vector<bool>
vector<bool>并不真正存储bool, 为了节省空间, vector中的bool只占一个二进制位.
vector必须支持operator[],vector<bool>不允许. &v[0]也不允许.
deque<bool> OK.
19: 理解equality和equivalence的区别.
相等equality是以operator==, 等价是以A不大于B,B也不大于A.
20: 为包含指针的关联容器指定比较类型
set默认的比较函数是less.
set<string *> ssp实际为set<string*, kess<strubg*>>ssp;
所以需要编写string *的比较函数
1 struct stringPtrLess: 2 3 public binary_function<const string*, cosnt string*, bool>{ 4 5 bool operator()(const string *ps1, const string *ps2)const 6 7 { 8 9 return *ps1 > *ps2;10 11 }12 13 }14 15 set<string *, stringPtrLess> ssp;
21: 总是让比较函数在等值情况下返回false
22: 切勿直接修改set或multiset中的键
map, multimap中键也不可更改.元素类型为pair<const k, v>
23: 考虑用排序的vector替代关联容器
标准关联容器通常被实现为平衡的二叉树查找.
sort算法排序vector.
24: 当效率至关重要时, 请在map::operator[]与map::insert之间谨慎抉择
如果key不存在, map::operator[]相当于先构造再赋值
1 map<int, string> m;2 3 m[1] = "123";
相当于
1 pair<map<int, string>::iterator, bool> result = m.insert(map<int, string>::value_type(1, string())); 2 3 result.first->second = "123"; 4 5 6 7 insert: 8 9 map<int, string> m;10 11 m.insert(map<int, string>::value_type( 1, "123"));
如果元素已经存在, 优先选择operator[], 代码简洁.
25: 熟悉非标准的哈希容器
26: iterator优先于const_iterator, reverse_iterator, const_reverse_iterator
iterator和const_iterator不是同一类型, 不能用const_cast转换.
27: 使用distance和advance将容器的const_iterator转换沉iterator
1 deque<int> d;2 3 deque<int>::const_itertaor ci;4 5 deque<int>::iterator i(d.begin());6 7 advance(i, distance< deque<int>::const_itertaor >(i, ci); /*必须将i转换成deque<int>::const_itertaor, 否则distance模板无法编译.*/
distance两个迭代器之间的距离, advance将迭代器移动指定距离.
28: 正确理解有reverse_iterator的base()成员函数所产生的iterator的用法
相差一个位置
但是不可用v.erase(--ri.base()); C/C++规定了从函数返回的指针不能被修改.
所以改为v.erase((++ri).base());
29: 对于逐个字符的输入请考虑使用istreambuf_iterator
30:确保目标区间足够大
确保vector中元素存在, 如果不存在, 调用resize, 或者使用back_iterator.
reserve只是增加容器的容量, 而容器size并未改变.
31: 了解各种与排序有关的选择
sort 完全排序
stable_sort 稳定排序, 如果A和B相等且排序前A在B前, 则排序后A仍然在B前
partial_sort 之排序部分,sortEnd
为排序的最后一位的下一个
.
1 template<class RandomAccessIterator> 2 3 void partial_sort( 4 5 RandomAccessIterator first, 6 7 RandomAccessIterator sortEnd, 8 9 RandomAccessIterator last10 11 );12 13 template<class RandomAccessIterator, class BinaryPredicate>14 15 void partial_sort(16 17 RandomAccessIterator first, 18 19 RandomAccessIterator sortEnd,20 21 RandomAccessIterator last22 23 BinaryPredicate comp24 25 );26 27 28 29 nth_element 与partial_sort不同之处在于只是将某些元素移至到前面,但并未排序.30 31 template<class RandomAccessIterator>32 33 void nth_element(34 35 RandomAccessIterator _First,36 37 RandomAccessIterator _Nth,38 39 RandomAccessIterator _Last40 41 );42 43 template<class RandomAccessIterator, class BinaryPredicate>44 45 void nth_element(46 47 RandomAccessIterator _First,48 49 RandomAccessIterator _Nth, //排序的最后一个元素50 51 RandomAccessIterator _Last,52 53 BinaryPredicate _Comp54 55 );
partition 将满足条件的元素移至前面, 返回迭代器指向第一个不满足条件的元素
stable_partition
32: 如果确实需要删除元素, 需要在remove这类算法后调用容器的erase
remove, remove_if, unique等算法不是真正的删除, 算法不会改变容器元素.需要调用
v.erase(remove(v.begin(), v.end(), 22));
list::remove和list_unique会真正删除元素.
33: 对包含指针的容器使用remove这一类算法时要特别小心
会将某些指针覆盖无法delete
应该先用for_each将不符合的元素指针delete再置为0, 之后remove和erase.
34: 了解哪些算法要求使用排序的区间作为参数
35: 通过mismatch或lexicographical_compare实现简单的忽略大小写的字符串比较
mismatch
mismatch(s1.begin(), s1.end(), s2.begin(), not2(ptr_fun(charCompare))); 返回pair, first为s1的第一个不匹配的迭代器, second为s2的第一个不匹配的迭代器, 如果都匹配, 则返回pair(s1.end(), s2.begin()+s1.end()-s1.begin());
lexicographical_compare接受判别式判断两个区间大小, 直到判别式不为true, 如果第一个却见结束了, 返回true.
1 bool charLess(char c1, char c2) 2 3 { 4 5 return tolower(static_cast<unsigned char> (c1)<tolower(static_cast<unsigned char> (c2); 6 7 } 8 9 bool stingCompare(const string &s1, const string &s2)10 11 {12 13 return lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), charLess);14 15 }16 17
36: 理解copy_if算法的正确实现
STL中没有copy_if, 利用remove_copy_if
37: 使用accumulate或for_each进行区间统计
38: 遵循按值传递的原则来设计函数子类
39: 确保判别式是纯函数
判别式是返回bool类型的函数
纯函数是返回值仅和参数相关的函数.只有参数发生变化,返回值才变化.
判别式类是函数子类, operator()为判别式.
40: 若一个类是函数子, 则应使它可配接
1 bool charLess(const char c) 2 3 { 4 5 return c>40; 6 7 } 8 9 int _tmain(int argc, _TCHAR* argv[])10 11 {12 13 find_if(s.begin(), s.end(), not1(charLess)); /*无法编译,charLess缺少not所需的类型, ptr_fun完成了类型定义的工作*/14 15 find_if(s.begin(), s.end(), not1(ptr_fun(charLess)));16 17 }
最简单的方法是让函数子从特定的基类继承, 如果函数子的operator()只有一个实参, 则继承std::unary_function, 如果有两个实参, 继承std::binary_function, 以上基类都为模板, 继承是需指定实参和返回类型.
struct stringCompare:public std::binary_function<string, string, bool>/*注意, 去掉const和&, 但是如果类型为指针, 则不能去掉*/{public: bool operator()(const string& s1, const string& s2) const { return s1<s2; }};int _tmain(int argc, _TCHAR* argv[]){ vector<string> v; //... find_if(v.begin(), v.end(), bind2nd(stringCompare(), "123"));}
41: 理解ptr_fun, mem_fun, mem_fun_ref的来由
mem_fun 参数为成员函数指针, 并返回mem_fun_t类型对象, mem_fun_t的operator()函数中调用了通过参数传递进来的对象上的成员函数
mem_fun_ref同mem_fun, 区别为一个是对象引用, 一个是函数指针.
1 class Widget 2 3 { 4 5 public: 6 7 void test() //paramter: this 8 9 {10 11 }12 13 };14 15 vector<Widget> v;16 17 //...18 19 for_each(v.begin(), v.end(), mem_fun_ref(&Widget::test));20 21 22 23 vector<Widget *> pv;24 25 //...26 27 for_each(pv.begin(), pv.end(), mem_fun(&Widget::test));28 29
42: 确保less<T>和operator<具有相同的意义
43: 算法调用优先于手写的循环
44: 容奇的成员函数优先于同名的算法
45: 正确区分count, find, binary_seach, lower_bound, upper_bound, equal_range
count, find使用相等性搜索, 而binary_seach, lower_bound, upper_bound, equal_range使用了等价性.
46: 考虑使用函数对象而不是函数作为STL算法的参数
函数作为参数时, 会产生函数调用, 且函数指针抑制了inline机制.
47: 避免产生write_only代码
48: 总是包含正确的头文件
49: 学会分析与STL相关的编译器诊断信息
50: 熟悉与STL相关的web站点
来源:https://www.cnblogs.com/zengyou/archive/2011/09/29/2195602.html