《STL标准程序库》笔记6-vector

STL容器(STL container

容器的共通能力和操作

STL容器的共通能力,三个最核心的能力:

1.所有容器提供的都是值(value)而不是引用(reference),STL容器的每一个元素都必须能够被拷贝。

2.每个容器都提供可返回迭代器的函数,运用那些迭代器你就可以巡访元素。这是STL演算法赖以生存在关键介面。

3.一般而言,各项操作并非绝对安全。调用者必须确保传给操作函数的参数符合需求。违反这些需求会导致未定义的行为。

以下操作为所有容器共有,它们均满足上述核心能力。

初始化(initialization

每个容器类型都提供了一个默认(default)构造函数,一个copy构造函数和一个析构函数:

以某个array的元素为初值,完成初始化动作:

int array[] = {2,3,17,33,45,77};

std::set c(array,array+sizeof(array)/sizeof(array[0]));

以标准输入流完成初始化动作:

std::deque c((std::istream_iterator(std::cin)),

(std::istream_iterator()));

注意:不要漏了括号,否则你会得到一堆奇怪的警告或错误。

如果不加括号c被视为一个函数,返回值是deque

Vectors

vector模塑出一个dynamic array。不过C++ Standard并未要求不许以dynamic array实现vector,只是规定了相应条件和操作复杂度。

使用vector之前,必须包含头文件

其中类型vector是一个定义于namespace std内的template

namespace std {

template < class T, class Allocator = allocator >

class vector;

}

Vectors的能力

vectors将其元素赋值到内部的dynamic array中。元素之间总是存在某种顺序,所以vectors是一种有序群集(ordered collection)。vector支持随机存取,所以对任何一个STL演算法都可以奏效。

大小(Size)和容量(Capacity

vector之中用于操作大小的函数有size()empty()max_size()。另一个与大小有关的函数是capacity(),返回vector实际能够容纳的元素数量。如果超出这个数量,vector就有必要重新配置内部内存。

vector的容量之所以很重要,有以下两个原因:

1. 一旦内存重新配置,和vector元素相关的引用(references)、指针(pointers)、迭代器(iterators)都会失效。

2. 内存重新配置很耗时间。

如果执行速度对你而言至关重要,那么就必须考虑容量问题。

你可以使用reserve()保留适当容量,避免一再重新配置内存:

std::vector v;

v.reserve(80);

另一种避免重新配置内存的方式是,初始化期间就向构造函数传递附加参数,构造出足够的空间。如果你的参数一个数值,它将成为vector的起始大小。

std::vector v(5);

要获得这种能力,此种元素必须提供一个默认的构造函数。如果你这么做只不过是为了保留足够的内存,那倒不如使用reserve()

vector的容量,概念上和strings类似,不过有一个大不同点:vector不能使用reserve()来缩减容量,这一点和strings不同。在许多实现方案中即使你不调用reserve(),当你第一次插入元素时也会一口气分配整块内存(例如2K)。

这里有一个间接缩减vector容量的小窍门。两个vectors交换内容后,两者的容量也会互换:

template 

void shrinkCapacity(std::vector& v) {

std::vector tmp(v);

v.swap(tmp);

}

你甚至可以利用下面的语句直接缩减容量:

std::vector(v).swap(v);

注意:swap()之后原先所有的referencespointersiterators都失效。

Vector的操作函数

操作

效果

vector c

产生一个空的vector,其中没有任何元素

vector c1(c2)

产生另一个同型vector的副本

vector c(n)

利用元素的默认构造函数生成大小为nvector

vector c(n,elem)

产生一个大小为n每个元素都是elemvector

vector c(beg,end)

产生一个vector,以区间[beg,end)作为元素初值

c.~vector()

销毁所有元素,并释放内存

元素存取(Element Access

按照CC++的惯例,第一元素的索引为0,最后元素的索引为size()-1所以第n个元素的索引是n-1。对于non-const vectors,这些函数都返回元素的引用(reference)。也就是说你可以使用这些操作函数来更改元素内容。

操作

效果

c.at(idx)

返回索引idx的元素,如果idx越界,抛出out_of_range

c[idx]

返回索引idx的元素。不进行范围检查

c.front()

返回第一元素,不检查第一个元素是否存在

c.back()

返回最后一个元素,不检查第一个元素是否存在

迭代器相关函数(Iterator Functions

操作

效果

c.begin()

返回一个随机存取迭代器,指向第一个元素

c.end()

返回一个随机存取迭代器,指向最后元素的下一位置

c.rbegin()

返回一个逆向迭代器,指向逆向迭代器的第一元素。

c.rend()

返回一个逆向迭代器,指向逆向迭代器的最后元素下一位置。

vector迭代器持续有效,除非发生两种情况:(1)使用者在一个较小索引位置上插入或移除元素,(2)由于容器变化引起内存重新分配。

插入(insert)和移除(remove)元素

操作

效果

c.insert(pos,elem)

pos位置上插入一个elem副本,并返回新元素位置

c.insert(pos,n,elem)

pos位置上插入nelem副本。无返回值

c.insert(pos,beg,end)

pos位置上插入区间[beg,end)内的所有元素的副本。无返回值。

c.push_back(elem)

在尾部添加一个elem副本。

c.pop_back()

移除最后一个元素(但不返回)。

c.erase(pos)

移除pos位置上的元素,返回下一元素的位置。

c.erase(beg,end)

移除[beg,end)区间内所有元素,返回下一元素位置。

c.resize(num)

将元素数量改为num(如果size()变大了,多出来的新元素都需以默认构造函数完成)

c.resize(num,elem)

将元素数量改为num(如果size()变大了,多出来的新元素都是elem的副本)

c.clear()

移除所有元素,将容器清空。

vector并未提供任何函数可以直接移除与某值相等的所有元素。这是演算法发挥威力的时候:

std::vector coll;

coll.erase( remove(coll.begin(),coll.end(),val ), coll.end() );

如果只是要移除与某值相等的第一个元素,可以这么做:

std::vector coll;

std::vector::iterator pos;

pos = find( coll.begin(), coll.end(), val );

if( pos != coll.end() ) {

coll.erase(pos);

}

Vectors当作一般Arrays使用

对于vector v中任意一个合法索引i,一下算式肯定为true

&v[i] == &v[0] + i

保证了这一点,就可推到出一系列重要结果。简单地说,任何地点只要你需要一个dynamic array,你就可以使用vector。例如你可以利用vector来存放常规的C字符串(类型为char*const char*):

std::vector v;

v.resize(41);

strcpy(&v[0],"hello, world");

printf("%s/n", &v[0]);

不过,这么运用vector你可得小心(和使用dynamic array一样小心),例如你必须确保vector大小足够,如果你用的是C-String,记住最后有个'/0'元素。

注意:千万不要把迭代器当作第一元素的位置来传递。vector迭代器是由实现版本定义的,也许并不是个一般的指针。

printf("%s/n",v.begin());

printf("%s/n",&v[0]);

异常处理(Exception Handling

vector只支持最低限度的逻辑错误检查。at()是唯一被标准规格要求可能抛出异常的一个函数。只有一般标准异常(例bad_alloc),或用户自定操作函数的异常,才可能发生。

如果vector调用的函数抛出异常,C++标准程序库做出如下保证:

1.如果push_back()插入元素时发生异常,搞函数不生效用。

2.如果元素的拷贝动作(包括copy构造函数和assignment运算符)不抛出异常,那么insert()要么成功,要么不生效用。

3.pop_back()绝不会抛出任何异常。

4.如果元素拷贝动作不抛出异常,erase()clear()就不抛出异常。

5.swap()不抛出异常。

6.如果元素拷贝动作绝对不会抛出异常,那么所有操作不是成功,就是不生效用。这类元素可被称为PODplain old data,简朴的老式数据)。POD泛指哪些无C++特熊的类型,例如C structure便是。

所有这些保证都基于一个条件:析构函数不得抛出异常。

Vectors运用实例:

#include 

#include 

#include 

#include 

using namespace std;

int main()

{

vector sentence;

sentence.reserve(5);

sentence.push_back("Hello,");

sentence.push_back("how");

sentence.push_back("are");

sentence.push_back("you");

sentence.push_back("?");

copy( sentence.begin(), sentence.end(),

  ostream_iterator(cout," ") );

cout << endl;

cout << " max_size(): " << sentence.max_size() << endl;

cout << " size(): " << sentence.size() << endl;

cout << " capacity(): " << sentence.capacity() << endl;

swap( sentence[1], sentence[3] );

sentence.insert( find(sentence.begin(),sentence.end(), "?"),

 "always" );

sentence.back() = "!";

copy( sentence.begin(), sentence.end(),

  ostream_iterator(cout," ") );

cout << endl;

cout << " max_size(): " << sentence.max_size() << endl;

cout << " size(): " << sentence.size() << endl;

cout << " capacity(): " << sentence.capacity() << endl;

}

Class Vector

C++标准程序库专门针对元素类型为boolvector设计了一个特殊版本,目的是获取一个优化的vector。其耗用空间远远小于以一般vector实现作出来的bool vector。特殊版本之用一个bit来存储一个元素。C++的最小可定址值通常以byte为单位。所以需针对referencesiterators做特殊考虑。

操作

效果

c.flip()

将所有bool元素值取反值,亦即求誧数

m[idx].flip()

将索引idxbit元素取反值

m[idx] = val

令索引idxbit元素值为val

m[idx1] = m[idx2]

令索引idx1的值为索引idx2

你可能感兴趣的:(《STL标准程序库》笔记6-vector)