STL-vector,stack,list,queue
vector是可以快速地在最后添加删除元素,并可以快速地访问任意元素
list是可以快速地在所有地方添加删除元素,但是只能快速地访问最开始与最后的元素
deque在开始和最后添加元素都一样快,并提供了随机访问方法,像vector一样使用[]访问任意元素,但是随机访问速度比不上vector快,因为它要内部处理堆跳转
deque也有保留空间.另外,由于deque不要求连续空间,所以可以保存的元素比vector更大,这点也要注意一下.还有就是在前面和后面添加元素时都不需要移动其它块的元素,所以性能也很高。
因此在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,一般应遵循下面
的原则:
1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
2、如果你需要大量的插入和删除,而不关心随即存取,则应使用list
3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
cber:很遗憾地告诉你,你的回答只对了一半:((,看来老虎也有打盹的时候啊。
下面是“GotW #074”的主要内容,我知道你一定更喜欢看原文,但我贴此文是为了照顾一些刚学C++的朋友。
GotW #074主要内容:
// Example 1: [] vs. at()
//
void f( vector<int>& v )
{
v[0]; // A
v.at(0); // B
}
在这个例子中,如果v不是空的,则A句和B句无分别。若v是空的,那么B句会throw一个std::out_of_range的exception;C++ Standard从效率和兼容‘内建型别’上考虑,并不要求A句throw出exception。即vector<T>::at()有下标越界检查(Bounds checking),而vector<T>::operator []()一般(注意是一般,C++ Standard并没有说operator[]一定不能做下标越界检查)没有做下标越界检查。如果向operator[]传入非法的下标,其行为“不可预测”
// Example 2: Some fun with vectors
vector<int> v;
v.reserve( 2 );
assert( v.capacity() == 2 );
1. 如cber所说,v.reserve(2)表示v的内部缓冲区至少能容纳2个元素,而不一定刚好容纳2个元素(可能更多)。vector的大小是按指数增长的,即cber所说“选择2的n次方来分配空间”,这句话应该改为assert( v.capacity() >= 2 );
2. 其实这个assert( v.capacity() >= 2 );也是多余的,因为C++ Standard保证执行v.reserve(n);后, 一定有v.capacity() >= n;即在vector<T>::reserve()中已经做了类似的assert,你又何必自己再来做重复的工作呢?除非你对你的C++ Standard Libaray的健壮性不放心:)。
v[0] = 1;
v[1] = 2;
这两条语句有明显的错误:v.reserve()只是增加了vector的存储空间,并没有增加其中元素的个数。这时v仍然是空的(v.size() == 0,v.capacity() >= 2),所以这里下标越界了。可以考虑改为v.push_back(1);v.push_back(2);
for( vector<int>::iterator i = v.begin(); i < v.end(); i++ )
{
cout << *i << endl;
}
1. 这个循环不会打印任何东西,因为此时v是空的。
2. 尽量使用const,这里的iterator并没有改动v的元素,所以应该用const_iterator
3. 循环的结束条件处,比较两个iterators应该用 != 而非 < ,只有random-access iterator才能用 “<”来比较,而作为循环的结束条件,更加普遍的做法是用 “!=”来比较iter与v.end()。
4. 用++i取代i++,两者效率不同。
5. 避免不必要的重复计算,因为v.end()的值在循环中没有变,所以在循环之前对v.end()求一次值就可以了。
6. 用'\n'替代endl,因为使用endl会强制flush(冲洗) ostream的内部输出缓冲区,如果你不是每打印一个*i就需要flush一次缓冲区的话,用'\n'。
7. 使用标准的演算法,可以避免以上所有问题:
copy( v.begin(), v.end(), ostream_iterator<int>(cout, "\n") );
另外,似乎没有copy( v.begin(), v.end(), ostream_iterator<int>(cout, endl) );这种写法,所以当你确实需要每打印一行就flush一次缓冲区时,自己写一个copy()吧。
cout << v[0];
一般情况下,输出1,其实v[0]是越界了。
v.reserve( 100 );
assert( v.capacity() == 100 );
同上,这里的assert是不正确且多余的。
这里v重新分配(realloc)了缓冲区,但它认为自己只有0个元素,故原来的v[0] == 1并没有复制到新的缓冲区,所以新的v[0] == 0;所以下一句
cout << v[0];
会打印0,这一定会气倒一片人的。
v[2] = 3;
v[3] = 4;
// ...
v[99] = 100;
首先下标越界,其次按cber说的可以用for_each()
for( vector<int>::iterator i = v.begin();
i < v.end(); i++ )
{
cout << *i << endl;
}
改为:
copy( v.begin(), v.end(), ostream_iterator<int>(cout, "\n") );
关于用STL演算法取代自己编写循环的进一步讨论,请参看CUJ 2001十月,Scott Meyers写的《STL Algorithms vs. Hand-Written Loops》一文
好多的陷阱啊,一不小心就掉进去了,非得好好看看Effective STL / Exceptional C++不可。
我晕了,你晕了没有?
vector: Constructors 构造函数 Operators 对vector进行赋值或比较 assign() 对Vector中的元素赋值 at() 返回指定位置的元素 back() 返回最末一个元素 begin() 返回第一个元素的迭代器 capacity() 返回vector所能容纳的元素数量(在不重新分配内存的情况下) clear() 清空所有元素 empty() 判断Vector是否为空(返回true时为空) end() 返回最末元素的迭代器(译注:实指向最末元素的下一个位置) erase() 删除指定元素 front() 返回第一个元素 get_allocator() 返回vector的内存分配器 insert() 插入元素到Vector中 max_size() 返回Vector所能容纳元素的最大数量(上限) pop_back() 移除最后一个元素 push_back() 在Vector最后添加一个元素 rbegin() 返回Vector尾部的逆迭代器 rend() 返回Vector起始的逆迭代器 reserve() 设置Vector最小的元素容纳数量 resize() 改变Vector元素数量的大小 size() 返回Vector元素数量的大小 swap() 交换两个Vector list: assign() 给list赋值 back() 返回最后一个元素 begin() 返回指向第一个元素的迭代器 clear() 删除所有元素 empty() 如果list是空的则返回true end() 返回末尾的迭代器 erase() 删除一个元素 front() 返回第一个元素 get_allocator() 返回list的配置器 insert() 插入一个元素到list中 max_size() 返回list能容纳的最大元素数量 merge() 合并两个list pop_back() 删除最后一个元素 pop_front() 删除第一个元素 push_back() 在list的末尾添加一个元素 push_front() 在list的头部添加一个元素 rbegin() 返回指向第一个元素的逆向迭代器 remove() 从list删除元素 remove_if() 按指定条件删除元素 rend() 指向list末尾的逆向迭代器 resize() 改变list的大小 reverse() 把list的元素倒转 size() 返回list中的元素个数 sort() 给list排序 splice() 合并两个list swap() 交换两个list unique() 删除list中重复的元素 stack: 操作 比较和分配堆栈 empty() 堆栈为空则返回真 pop() 移除栈顶元素 push() 在栈顶增加元素 size() 返回栈中元素数目 top() 返回栈顶元素 queue: back() 返回最后一个元素 empty() 如果队列空则返回真 front() 返回第一个元素 pop() 删除第一个元素 push() 在末尾加入一个元素 size() 返回队列中元素的个数