STL之“要”(一)

来源:互联网 发布:数据库原理是什么 编辑:程序博客网 时间:2024/06/10 03:13

这篇博文是《effective STL》的读后感。Bow to Scott。

1、 要多使用vector::reserve(size_t n),以减少不必要的拷贝。

vector::reserve(size_t n)在标准(http://www.cplusplus.com/reference/stl/vector/reserve/)里的解释是:vector将预存n个元素空间,当容器满时,才会执行扩张操作。另外,这一技巧特别适用于从文件中取元素。

#include <iostream>#include <fstream>#include <vector>using namespace std;int main (){  vector<int> content;  size_t filesize;  ifstream file ("test.bin",ios::in|ios::ate|ios::binary);  if (file.is_open())  {    filesize=file.tellg();    content.reserve(filesize);    file.seekg(0);    while (!file.eof())    {      content.push_back( file.get() );    }    // print out content:    vector<int>::iterator it;    for (it=content.begin() ; it<content.end() ; it++)      cout << hex << *it;  }  return 0;}
2、 要用empty()来代替检查size()是否为0。
对于大多数的容器,size()时间复杂度是常数,但对于一些容
器,比如list,就是线性的。造成这种情况的原因是与splice函数的折中。
3、 要尽量使用区间成员函数代替它们的单元素兄弟。

总之尽量少自己写循环,多用assign和insert函数的应用。具体理由和一个面试题——将一个字符串中的空格替换为“%20”——很像。另外作者问了一个经典问题:给定两个vector,v1和v2,使v1的内容和v2的后半部分一样的最简单方式是什么?

v1.assign(v2.begin() + v2.size() / 2, v2.end()); v.insert(v.begin(), data, data + numValues);

4、当使用new得的指针的容器时,要在销毁容器前delete那些指针。
本条款是为了防止内存泄露。手工delete太繁了,然而除非使
用引用计数器,很难实现自动回收(见《effective STL》中的讨论)。很开心C++11标准引入了shared_ptr;但是使用shared_ptr要注意相互引用造成死锁的问题(采用weak_ptr解决,有兴趣的童鞋可以查查,就不在这讨论了)。
5、删除顺序容器中的元素时要考虑选择erase-remove或erase-
remove_if。
顺序容器中,remove并不真正地将元素删除,而是将其放到容器的尾部。erase是真正删除元素。但注意,list的删除比较特殊,直接调用remove
        (1)将容器中值为1963的值全部删除:
        对于顺序容器:c.erase(remove(c.begin(), c.end(), 
1963),c.end());  
        对于list:  c.remove(1963);  
        (2)将顺序容器中满足此函数为true的值删除:bool badValue
(int x)
        对于顺序容器:c.erase(remove_if(c.begin(), c.end(), badValue),c.end);

        对于list: c.remove_if(badValue); 

6、如果决定用new生成对象序列,那么最好用vector或者string进行管理。(而不是数组)

vector和string管理自己的内存。当元素添加到那些容器中时它们的内存会增长,而且当一个vector或string销毁时,它的析构函数会自动销毁容器中的元素,回收存放那些元素的内存。

7、要记得用swap技巧来休整过剩容量

有时候,如果你的vector有时候容纳了10万个的可能的候选元素,但后来它只挑出10个,其他的erase掉,但vector的容量会继续保持在至少100,000。这无疑是一种浪费。可以采用这样的技巧来“收缩到合适(shrink to fit)”:

vector<Contestant>(contestants).swap(contestants);

8、要为指针的关联容器指定比较类型

我们经常用map或者set存指针。而关联容器有隐含的比较仿函数,例如set<string*> ssp其实是set<string*, less<string*>, allocator<string*> > ssp。当我们想按顺序输出存放的值时,应当为其指定比较仿函数。(或者说养成好习惯,坚持为指针的关联容器指定比较类型)。比如:

struct StringPtrLess:public binary_function<const string*,const string*,bool> {        bool operator()(const string *ps1, const string *ps2) const        {                return *ps1 < *ps2;        }};typedef set<string*, StringPtrLess> StringPtrSet;StringPtrSet ssp;

这样用迭代器访问ssp中元素时就是按顺序访问的。

9、永远让比较函数对相等的值返回false

如上一条中的<号不能定为<=,主要原因是等价的判定会因=出现问题,不深究,牢记。

10、当查找速度真的很重要时,可以考虑排序的vector或者hash代替关联容器

这个不多说,面试题或实际应用见过太多

11、要根据实际情况仔细选择map::operator[]和map-insert

虽然二者的效果一样,但在不同情况下效率是不一样的。主要问题在于[]会新开辟临时变量。

结论是:如果你要更新已存在的map元素,operator[]更好,但如果你要增加一个新元素,insert则有优势。