Vector 使用总结

一、vector的基本概念

vector是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。和string对象一样,标准库负责管理存储元素的相关内存。我们把vector称为容器,是因为它可以包含其他对象。一个容器中的所有对象都必须是同一种类型的。使用vector之前,必须包含相应的头文件。

#include<vector>

usingstd::vector;

vector是一个类模板(classtemplate这个类和函数定义可用于不同的数据类型上。因此,我们可以定义保存string对象的vector,或保存int值的vector,又或是保存自定义的类类型对象(如Sales_item对象)的vector。声明从类模板产生的某种类型的对象,需要提供附加信息,信息的种类取决于模板。以vector为例,必须说明vector保存何种对象的类型,通过将类型放在类模板名称后面的尖括号中来指定类型:

vector<int>ivec;//ivecholdsobjectsoftypeint

vector<Sales_item>Sales_vec;//holdsSales_items

和其他变量定义一样,定义vector对象要指定类型和一个变量的列表。上面的第一个定义,类型是vector<int>,该类型即是含有若干int类型对象的vector,变量名为ivec。第二个定义的变量名是Sales_vec,它所保存的元素是Sales_item类型的对象。

vector不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型。vector类型的每一种都指定了其保存元素的类型。因此,vector<int>vector<string>都是数据类型。

二、vector的基本操作

1vector对象的定义和初始化

vector类定义了好几种构造函数,用来定义和初始化vector对象。如下示出几种初始化vector对象的方式

vector<T>v1;

vector保存类型为T的对象。默认构造函数v1为空。

vector<T>v2(v1);

v2v1的一个副本。

vector<T>v3(n,i);

v3包含n个值为i的元素。

vector<T>v4(n);

v4含有值初始化的元素的n个副本。

a创建确定个数的元素

若要创建非空的vector对象,必须给出初始化元素的值。当把一个vector对象复制到另一个vector对象时,新复制的vector中每一个元素都初始化为原vector中相应元素的副本。但这两个vector对象必须保存同一种元素类型:

vector<int>ivec1;//ivec1holdsobjectsoftypeint

vector<int>ivec2(ivec1);//ok:copyelementsofivec1intoivec2

vector<string>svec(ivec1);//error:svecholdsstrings,notints

可以用元素个数和元素值对vector对象进行初始化。构造函数用元素个数来决定vector对象保存元素的个数,元素值指定每个元素的初始值:

vector<int>ivec4(10,-1);//10elements,eachinitializedto-1

vector<string>svec(10,"hi!");//10strings,eachinitializedto"hi!"

关键概念:vector对象动态增长

vector对象(以及其他标准库容器对象)的重要属性就在于可以在运行时高效地添加元素。因为vector增长的效率高,在元素值已知的情况下,最好是动态地添加元素。

虽然可以对给定元素个数的vector对象预先分配内存,但更有效的方法是先初始化一个空vector对象,然后再动态地增加元素

b、值初始化

如果没有给出元素的初始化式,那么标准库将提供一个值初始化的(valueinitialized)元素初始化式。这个由库生成的初始值用于初始化容器中的每个元素。而元素初始化式的值取决于存储在vector中元素的数据类型。

如果vector保存内置类型(如int类型)的元素,那么标准库将用0值创建元素初始化值:

vector<string>fvec(10);//10elements,eachinitializedto0

如果向量保存类类型(如string)的元素,标准库将用该类型的默认构造函数创建元素初始值:

vector<string>svec(10);//10elements,eachanemptystring

cvector的操作

empty()

如果 为空,则返回 true, 否则返回 false 

v . size ()

返回 中元素的个数。

v . push _ back ( t )

在 的末尾增加一个值为 的元素。

v [ n ]

返回 中位置为 的元素。

v1 = v2

把 v1 的元素替换为 v2 中元素的副本。

v1 == v2

如果 v1 与 v2 相等,则返回 true 

!=, <, <=, >, >=

保持这些操作符惯有的含义。

1vector对象的size

emptysize操作类似于string类型的相关操作。成员函数size返回相应vector类定义的size_type的值。

使用size_type类型时,必须指出该类型是在哪里定义的。vector类型总是包括vector的元素类型:

vector<int>::size_type//ok

vector::size_type//error

2vector添加元素

push_back()操作接受一个元素值,并将它作为一个新的元素添加到vector对象的后面,也就是“插入(push)”到vector对象的“后面(back)”:

//read words from the standard input and store the elements in a vector

stringword;

vector<string>text;//emptyvector

while(cin>>word){

text.push_back(word);//appendwordtotext

}

该循环从标准输入读取一系列string对象,逐一追加到vector对象的后面。首先定义一个空的vector对象text。每循环一次就添加一个新元素到vector对象,并将从输入读取的word值赋予该元素。当循环结束时,text就包含了所有读入的元素。

3vector的下标操作

vector中的对象是没有命名的,可以按vector中对象的位置来访问它们。通常使用下标操作符来获取元素。vector的下标操作类似于string类型的下标操作。

vector的下标操作符接受一个值,并返回vector中该对应位置的元素。vector元素的位置从0开始。下例使用for循环把vector中的每个元素值都重置为0:

//reset the elements in the vector to zero

for(vector<int>::size_type ix=0;ix!=ivec.size();++ix)

ivec[ix]=0;

string类型的下标操作符一样,vector下标操作的结果为左值,因此可以像循环体中所做的那样实现写入。另外,和string对象的下标操作类似,这里用size_type类型作为vector下标的类型。

在上例中,即使ivec为空,for循环也会正确执行。ivec为空则调用size返回0,并且for中的测试比较ix0。第一次循环时,由于ix本身就是0,则条件测试失败,for循环体一次也不执行。

关键概念:安全的泛型编程

C++程序员习惯于优先选用!=而不是<来编写循环判断条件。

4下标操作不添加元素

初学C++的程序员可能会认为vector的下标操作可以添加元素,其实不然:

vector<int>ivec;//emptyvector

for(vector<int>::size_typeix=0;ix!=10;++ix)

ivec[ix]=ix;//disaster:ivec has no elements

上述程序试图在ivec中插入10个新元素,元素值依次为09的整数。但是,这里ivec是空的vector对象,而且下标只能用于获取已存在的元素。

这个循环的正确写法应该是:

for(vector<int>::size_typeix=0;ix!=10;++ix)

ivec.push_back(ix);//ok:adds new element with value ix

必须是已存在的元素才能用下标操作符进行索引。通过下标操作进行赋值时,不会添加任何元素。

警告:仅能对确知已存在的元素进行下标操作

对于下标操作符([]操作符)的使用有一点非常重要,就是仅能提取确实已存在的元素,例如:

vector<int>ivec;//empty vector

cout<<ivec[0];//Error: ivec has no elements!

vector<int>ivec2(10);//vector with 10 elements

cout<<ivec[10];//Error:ivec has elements 0...9

试图获取不存在的元素必然产生运行时错误。

附:常用方法

1.push_back()   在数组的最后添加一个数据

2.pop_back()    去掉数组的最后一个数据

3.at()                得到编号位置的数据

4.begin()           得到数组头的指针

5.end()             得到数组的最后一个单元+1的指针

6.front()        得到数组头的引用

7.back()            得到数组的最后一个单元的引用

8.max_size()     得到vector最大可以是多大

9.capacity()       当前vector分配的大小

10.size()           当前使用数据的大小

11.resize()  改变当前使用数据的大小,如果它比当前使用的大,者填充默认值

12.reserve()      改变当前vecotr所分配空间的大小

13.erase()         删除指针指向的数据项

14.clear()          清空当前的vector

15.rbegin()        将vector反转后的开始指针返回(其实就是原来的end-1)

16.rend()        将vector反转构的结束指针返回(其实就是原来的begin-1)

17.empty()        判断vector是否为空

18.swap()         与另一个vector交换数据




三、vector的注意点

http://www.cuj.com/experts/1910/austern.htm?topic=experts

The Standard Librarian: I/O and Function Objects: Containers of Pointers

Matthew Austern

    和标准C++运行库中的绝大部分东西一样,标准容器类是用类型来参数化的:你能创建一个std::vector<int>来容纳int类型的对象,创建一个std::vector<std::string>来容纳string对象,创建一个 std::vector<my_type>来容纳用户自定义类型的对象。

    创建std::vector<int *>std::vector<std::string *>
std::vector<my_type *>也是完全合理的。容纳指针的容器很重要也很常见。

不幸地是,虽然是常见技术,容纳指针的容器对新手来说也是造成混乱的最常见的根源之一。几乎没有哪个星期在C++新闻组中不出现这样的贴子的:为什么这样的代码导致内存泄漏:

{

std::vector<my_type*> v;

for (int i = 0; i < N; ++i)

v.insert(new my_type(i));

...

} // v is destroyed here

这个内存泄漏是编译器的bug吗?std::vector的析构函数不是会销毁v的元素的吗?

如果你仔细想过std::vector<T>大体上是如何工作的,并且你了解其中对指针并没有特别的规则的话(也就是说,对vector来说,my_type *只不过是另外一个T)就不难明白为什么vector<my_type *>有这样的行为以及为什么这段代码有内存泄漏了。然而,vector<my_type *>的行为可能会令对旧的容器库更熟悉的人感到惊讶的。

这篇文章解释了容纳指针的容器的行为是怎么样的,什么时候容纳指针的容器会有用,和在需要执行比标准容器在内存管理上的默认行更多的任务时,该做些什么。

1、容器和所有权

标准容器使用值语义。举例来说,当你向一个vector附加一个变量x时:

v.push_back(x)

你实际正在做的是附加的一个拷贝。这个语句存储了x的值(的一个拷贝),而不是x的地址。在你将x加入一个vector后,你能对x做如何想做的事(比如赋给它一个新值或让它离开生存域而销毁)而不影响vector中的拷贝。一个容器中的元素不能是另外一个容器的元素两个容器的元素必须是不同的对象,即使这些元素碰巧相等),并且将一个元素从容器中移除将会销毁这个元素(虽然具有相同的值的另外一个对象可能存在于别处)。最后,容器“拥有”它的元素:当一个容器销毁时,其中的所以元素都随它一起销毁了。

这些特性与平常的内建数组很相似,并且可能是太明显了而不值一提。我列出它们以清楚显示容器和数组有多么相似。新手使用标准容器时发生的最常见的概念是认为容器“在幕后”做了比实际上更多的事。

值语义不总是你所需要的:有时你需要在容器中存储对象的地址而不是拷贝对象的值。你能以和数组相同的方式,用容器实现引用语义:藉由显式要求。你能将任何类型的对象放入容器,而指针自己就是非常好的对象。指针占用内存;能被赋值;自己有地址;有能被拷贝的值。如果你需要存储对象的地址,就使用容纳指针的容器。不再是写:

std::vector<my_type> v;

my_type x;

...

v.push_back(x);

你能写:

std::vector<my_type*> v;

my_type x;

...

v.push_back(&x);

感觉上,没有任何变化。你仍然正在创建一个std::vector<T>;只不过现在T碰巧是一个指针类型,my_type *vector仍然“拥有”它的元素,但你必须明白这些元素是什么:它们是指针,而不是指针所指向的东西。

拥有指针和拥有指针所指的东西之间的区别就象是vector与数组或局部变量。假如你写:

{

my_type* p = new my_type;

}

当离开代码域时,指针p将会消失,但它所指向的对象,*p,不会消失。如果你想销毁这对象并释放其内存,你需要自己来完成,显式地写delete p或用其它等价的方法。同样,在std::vector<my_type *>中没有任何特殊代码以遍历整个vector并对每个元素调用delete。元素在vector消失时消失。如果你想在那些元素销毁前发生另外一些事,你必须自己做。

你可能奇怪为什么std::vector和其它标准容器没有设计得对指针做些特别的动作。首先,当然,有一个简单的一致性因素:理解有一致语义的库比理解有许多特例的库容易。如果存在特例,很难划出分界线。你将iterator或用户自定义的handle类型等同于指针吗?如果在通用规则上对 vector<my_type *>有一个例外,应该对vector<const my_type *>再有一个例外的例外吗?容器如何知道什么时候用delete p,什么时候用delete [] p

第二,并且更重要的是:如果std::vector<my_type *>确实自动地拥有所指向的对象,std::vector的用处就大为减少了。毕竟,如果你期望一个vector拥有一系列my_type的对象的话,你已经有vector<my_type>了。vector<my_type *>是供你需要另外一些不同的东西时用的,在值语义和强所有权不合适时。当你拥有的对象被多个容器引用时,或对象能在同一容器出现多次时,或指针开始时并不指向有效对象时,你可以用容纳指针的容器。(它们可能是NULL指针,指向原生内存的指针,或指向子对象的指针。)

想像一个特别的例子:

你正在维护一个任务链表,某些任务当前是活动的,某些被挂起。你用一个std::list<task>存放所有任务,用一个std::vector<task *>存放活动任务组成的任务子集。

你的程序有一个字符串表:std::vector<const char *>,每个元素p指向一个NULL结束的字符数组。依赖于你如何设计你的字符串表,你可能使用字符串文字,或指向一个巨大的字符数组内部,无论哪种方法,你都不能用一个循环遍历vector,并对每个元素调用delete p

你正在做I/O multiplexing,并且将一个std::vector<std::istream *>传给一个函数。input stream是在别处打开的,将于别处关闭,并且,也许其中之一就是&std::cin

如果容纳指针的容器多手多脚地delete了所指向的对象,上面的用法没一个能成为可能。

2、拥有所指向的对象

如果你创建了一个容纳指针的容器,原因通常应该是所指向的对象由别处创建和销毁的。有没有情况是有理由获得一个容器,它拥有指针本身,还拥有所指向的对象?有的。我知道的唯一一个好的理由,但也是很重要的一个理由:多态。

C++ 中的多态是和指针/引用语义绑定在一起的。假如,举例来说,那个task不只是一个类,而且它是一个继承体系的基类。如果p是一个task *,那么p可能指向一个task对象或任何一个从task派生的类的对象。当你通过p调用task的一个虚函数,将会在运行期根据p所指向的实际类型调用相应的函数。

不幸地是,将task作为多态的基类意味着你不能使用vector<task>。容器中的对象是存储的值;vector<task>中的元素必须是一个task对象,而不能是派生类对象。(事实上,如果你遵从关于继承体系的基类必须是抽象基类的忠告的话,那么编译器将不允许你创建task对象和vector<task>对象。)

面向对象的设计通常意味着在对象被创建到对象被销毁之间,你通过指针或引用来访问对象。如果你想拥有一组对象,除了容纳指针的容器外,你几乎没有选择。管理这样的容器的最好的方法是什么?

如果你正使用容纳指针的容器来拥有一组对象,关键是确保所有的对象都被销毁。最明显的解决方法,可能也是最常见的,是在销毁容器前,遍历它,并为每个元素调用delete语句。如果手写这个循环太麻烦,很容易作一个包装:

template <class T>

class my_vector : private std::vector<T*>

{

typedef std::vector<T> Base;

public:

using Base::iterator;

using Base::begin;

using Base::end;

...

public:

~my_vector() {

for (iterator i = begin(); i != end(); ++i)

delete *i;

}

};

这个技巧能工作,但是它比看起来有更多的限制和要求

问题是,只改析构函数是不够的。如果你有一个列出所有的正要被销毁的对象的容器,那么你最好确保只要指针离开了容器那个对象就要被销毁,并且一个指针绝不在容器中出现两次。当你用erase()clear()移除指针时,必须要小心,但是你也需要小心容器的赋值和通过iterator的赋值:象v1 = v2,和v[n] = p这样的操作是危险的。标准泛型算法,有很多会执行通过iterator的赋值的,这是另外一个危险。你显然不能使用std::copy()和 std::replace()这样的泛型算法;稍微不太明显地,你也不能使用std::remove()std::remove_if(),和 std::unique()

my_vector这样的包装类能够解决其中一些问题,但不是全部。很难看出如何阻止用户以危险的方式使用赋值,除非你禁止所有的赋值,而那时,你所得到的就不怎么象容器了。

问题是每个元素都必须被单独追踪,所以,也许解决方法是包装指针而不是包装整个容器。

标准运行库定义了一个对指针包装的类std::auto_ptr<T>。一个auot_ptr对象保存着一个T *类型的指针p,其构造函数deletep所指的对象。看起来这正是我们所要找的:一个包装类,其析构函数delete一个指针。自然会想到用 vector<auto_ptr<T> >取代vector<T *>

这是很自然的主意,但它是错误的。原因呢,再一次,是因为值语义。容器类假设它们能拷贝自己的元素。举例来说,如果你有一个vector<T>,那么T类型的对象必须表现得和一个平常的数值一样。如果t1是一个T类型的值,你最好能够写:

T t2(t1)

并且得到一个t1的拷贝t2

形式上,按C++标准中的说法,T要是Assignable的和CopyConstructible。指针满足这些要求(你能得到指针的一个拷贝)但 auto_ptr不满足。auto_ptr的卖点是它维护强所用权,所以不允许拷贝。有一个形式上是拷贝构造函数的东西,但auto_ptr的“拷贝构造函数”实际上并不进行拷贝。如果t1是一个 std::auto_ptr<T>,并且你写:

std::auto_ptr<T> t2(t1)

然后t2将不是t1的一个拷贝。不是进行拷贝,而是发生了所有权转移(t2得到了t1曾经有着的值,而t1被改成一个NULL指针)。auto_ptr 的物件是脆弱的:你只不过看了它一下就能改变它的值。


在某些实作上,当你试图创建一个vector<auto_ptr<T> >的时候,会得到编译期错误。这还是算你幸运;如果不幸运的话,事情看起来很好,直到运行期得到不可预知的行为。总之,标准容器类不能与拷贝构造函数不执行拷贝的类型合作。这也不是auto_ptr的设计目的,并且,标准甚至指出“用auto_ptr实例化标准运行库中的容器会得到未定义的行为。”当你需要异常安全机制时,你应该使用auto_ptr以在退出代码空间时delete指针;auto_ptr是因模拟了自动变量而得名的。你不应该试图在容器类中使用auto_ptr来管理指针;它不可行。

取代auto_ptr,你应该使用一个不同的“智能指针”,引用计数的指针类。带引用计数的指针跟踪多少个指针指向相同的对象。当你构造了一个引用计数指针的拷贝时,计数加1;当你销毁一个引用计数指针时,计数减1。当计数变成0时,指针所指的对象被自动销毁。

写一个引用计数的指针类不是特别困难,但也不是几乎什么都不用做;达到线程安全需要特别的技巧。幸运地是,使用引用计数并不意味着你需要写一个自己的引用计数指针类;几种这样的类已经存在并可免费使用。比如,你能使用Boostshared_ptr类。我期望 shared_ptr或其它类似的东西将成为C++标准的下个修订版本的组成部分。

当然,引用计数只是一种特别的垃圾回收。像所有形式的垃圾回收,它自动销毁你不再需要的对象。引用计数指针的主要优势是它们易于加入现有系统:share_ptr这样的机制只是一个小的单独的类,你能只在一个较大系统中某个部分中使用它。另一方面,引用计数是垃圾回收的一种最低效的形式(每个指针的赋值和拷贝都需要一些相关的复杂处理),最没有柔性的形式(在两个数据结构拥有互指指针时,你必须小心)。其它形式的垃圾回收在C++程序中工作得同样好。特别地,Boehm conservative garbage collector是免费的、可移植的,并被很好测试过。

如果你使用一个保守的垃圾回收器,你只需要将它链接入程序就可以了。你不需要使用任何特别的指针包装类;只需要分配内存而不用关心delete它。特别地,如果你创建一个vector<T *>,你知道所指向的对象只要vector还存在就不会被delete(垃圾回收器绝不会破坏还有指针指向它们的对象),并且你也知道它们将在 vector被销毁后的某个时候delete(除非,程序的其它部份仍然引用它们)

垃圾回收的优势(无论是引用计数还是保守的垃圾回收器,或其它方法)是它让你将对象的生存期处理得完全不确定:你不须要掌握在某个特定时间程序的哪个部份引用了这个对象。另一方面,垃圾回收的缺点也正是这一点!有时你确实知道对象的生存期,或至少知道在程序的某个特定状态结束后对象不应该继续存在。举例来说,你可能创建了一个复杂的分析树;也许它填满了多态对象,也许它是太复杂而无法单独掌握每个节点,但是你能确定在分析完后就将不再需要它们中的任何一个。

从手工管理的vetor<T *>vector<shared_pointer<T> >到保守的垃圾回收器,我们逐步放弃了vector拥有一组对象的观点;垃圾回收的前提是对象的“所有权”是无关紧要的。在某种意义上,它通过扔掉“所有权”问题而解决了这个难题。

如果你的程序确实有明确定义的状态,那么你可能有理由期望在某个状态结束时销毁一组对象。代替垃圾回收,另外一个可选方法是通过一个arenaWQ注:字典上为“竞技场、舞台”之意,译不好,不译)来分配对象:维护一个对象列表,以便能一次就销毁所有对象。

你可能想知道这个技术和前面提到的技术(遍历容器并为每个元素调用destory())有多大差异。有实质性差异吗?如果没有,让我花了这么多时间的那些危险和限制,又怎么说?

差异很小,但很重要:arena存储了一个对象集,目的是为了在以后delete它们,并且没有其它目的。它可以以标准容器的形式实现,但是它不暴露容器接口。你在vector<T *>上遇到问题是因为你能移除元素,拷贝覆盖元素,和运用泛型算法。Arena是一个强所有权协议。Arena容器为每个它所管理的对象容纳了一个并且只一个指针,并且它拥有所有这些对象;它不允许你移除、复制、覆盖或用选择子遍历它容纳的指针。要使用arena中的对象,你需要在别处存储指向它们的指针--在容器中,在树中,或在任何合适的数据结构中。使用权和所有权完全分离。

arena 是个通用的主意。arena可能简单地是个容器,只要记住别使用不安全的成员函数,或者它可以是个包装类以试图运行得更安全。很多这样的包装类已经被写出来了[7]Listing 1是一个arena类的简化例子,使用了一个实现上的技巧以使得不必为每个指针类型使用一个不同的arena类。举例来说,你可能写:

arena a;

...

std::vector<int*> v;

v.push_back(a.create<int>(3));

...

a.destroy_all();

我们几乎回到了起点:使用一个容纳指针的vector,所指向的对象由别处拥有和管理。

3、总结

指针在C++程序中很常见,标准容器同样如此;不用惊奇,它们的组合物,容纳指针容器同样很常见。

新手最大的困难是容纳指针的容器时的所有权问题:应该什么时候delete所指向的对象?处理容纳指针的容器的绝大部分技术都可以归结到一个原则:如果你有一个容纳指针的容器,所指向的对象应该由其它地方所拥有。

如果正在处理非多态对象集(类型为my_type),你应该将对象的值存入容器,比如list<my_type>deque<my_type>。如果需要,你也以使用第二个容器以存储指向那些对象的指针。

不要试图将auto_ptr放入标准容器。

如果有一组多态对象,你需要用容纳指针的容器来管理它们(但是,那些指针可以被包装在某种handle类或智能指针类中。)当对象生存期不能预知时,或不重要时,最容易的方法是使用垃圾回收。垃圾回收的两个最简单的选择是引用计数指针类和保守的垃圾回收器。对你来说哪个是最佳选择取决于工具的可用性。

如果你有一组多态对象,并需要控制它们的生存期,最简单的方法是使用arena。一个简单的arena类例子展示于Listing 1

Listing 1: A simple arena class

#include <vector>

class arena {

private:

struct holder {

virtual ~holder() { }

};

template <class T>

struct obj_holder : public holder {

T obj;

template <class Arg1>

obj_holder(Arg1 arg1)

: obj(arg1) { }

};

std::vector<holder*> owned;

private:

arena(const arena&);

void operator=(const arena&);

public:

arena() { }

~arena() {

destroy_all();

}

template <class T, class Arg1>

T* create(Arg1 arg1) {

obj_holder<T>* p = new obj_holder<T>(arg1);

owned.push_back(p);

return &p->obj;

}

void destroy_all() {

std::vector<holder*>::size_type i = 0;

while (i < owned.size()) {

delete owned[i];

++i;

}

owned.clear();

}

};

四、vector的定义

template<class T, class A = allocator<T> >

class vector {

public:

typedef A allocator_type;

typedef A::size_type size_type;

typedef A::difference_type difference_type;

typedef A::reference reference;

typedef A::const_reference const_reference;

typedef A::value_type value_type;

typedef T0 iterator;

typedef T1 const_iterator;

typedef reverse_iterator<iterator, value_type,

reference, A::pointer, difference_type>

reverse_iterator;

typedef reverse_iterator<const_iterator, value_type,

const_reference, A::const_pointer, difference_type>

const_reverse_iterator;

explicit vector(const A& al = A());

explicit vector(size_type n, const T& v = T(), const A& al = A());

vector(const vector& x);

vector(const_iterator first, const_iterator last,

const A& al = A());

void reserve(size_type n);

size_type capacity() const;

iterator begin();

const_iterator begin() const;

iterator end();

iterator end() const;

reverse_iterator rbegin();

const_reverse_iterator rbegin() const;

reverse_iterator rend();

const_reverse_iterator rend() const;

void resize(size_type n, T x = T());

size_type size() const;

size_type max_size() const;

bool empty() const;

A get_allocator() const;

reference at(size_type pos);

const_reference at(size_type pos) const;

reference operator[](size_type pos);

const_reference operator[](size_type pos);

reference front();

const_reference front() const;

reference back();

const_reference back() const;

void push_back(const T& x);

void pop_back();

void assign(const_iterator first, const_iterator last);

void assign(size_type n, const T& x = T());

iterator insert(iterator it, const T& x = T());

void insert(iterator it, size_type n, const T& x);

void insert(iterator it,

const_iterator first, const_iterator last);

iterator erase(iterator it);

iterator erase(iterator first, iterator last);

void clear();

void swap(vector x);

protected:

A allocator;

};

The template class describes an object that controls a varying-length sequence of elements of type T. The sequence is stored as an array of T.

The object allocates and frees storage for the sequence it controls through a protected
object named allocator, of class A. Such an allocator object must have the same external 
interface asan object of template class allocator. Note that allocator is not copied when the 
object is assigned.

Vector reallocation occurs when a member function must grow the controlled sequence beyond its current storage capacity. Other insertions and erasures may alter various storage addresses within the sequence. In all such cases, iterators or references that point at altered portions of the controlled sequence become invalid.



阅读更多
换一批

没有更多推荐了,返回首页