概述
C++内置的数组支持容器的机制,但是它不支持容器抽象的语义。要解决此问题我们自己实现这样的类。在标准C++中,用容器向量(vector)实现。
容器向量也是一个类模板。vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,但是一个容器中的对象必须是同一种类型。简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。
vector是一个类模板,不是一种数据类型。可用来定义任意多种数据类型。vector类型的每一种都指定了其保存元素的类型。因此vector <int >等都是数据类型。
vector对象初始化
vector 类定义了好几种构 造函数,用来定义和初始化 vector 对象。
初始化 vector 对象的方式
vector <T > v1 ; |
vector 保存类型为 T 的对象。默认构造函数 v1 为空。 |
vector < T > v2 ( v1 ); |
v2 是 v1 的一个副本。 |
vector < T > v3 ( n , i ); |
v3 包含 n 个值为 i 的元素。 |
vector < T > v4 ( n ); |
v4 含有值初始化的元素的 n 个副本。 |
创建确定个数的元素
若要创建非空的 vector 对象,必须给出初始化元素的值。当把一个 vector 对象复制到另一个 vector 对象时,新复制的 vector 中每一个元素都初始化为原 vector 中相应元素的副本。但这两个 vector 对象必须保存同一种元素类型:
vector<int> ivec1; // ivec1 holds objects of type int
vector<int> ivec2(ivec1); // ok: copy elements of ivec1 into ivec2
vector<string> svec(ivec1); // error: svec holds strings, not ints
可以用元素个数和元素值对 vector 对象进行初始化。构造函数用元素个数来决定 vector 对象保存元素的个数,元素值指定每个元素的初始值:
vector<int> ivec4(10, -1); // 10 elements, each initialized to -1
vector<string> svec(10, "hi!"); // 10 strings, each initialized to "hi!"
值初始化
如果没有给出元素的初始化式,那么标准库将提供一个 值初始化的 ( value initialized )元素初始化式。这个由库生成的初始值用于初始化容器中的每个元素。而元素初始化式的值取决于存储在 vector 中元素的数据类型。
如果 vector 保存内置类型(如 int 类型) 的元素,那么标准库将用 0 值创建元素初始 化值:
vector<string> fvec(10); // 10 elements, each initialized to 0
如果向量保存类类型(如 string )的元素,标准库将用该类型的默认构造函数 创建 元素初始值:
vector<string> svec(10); // 10 elements, each an empty string
vector对象操作
vector 基本操作
v. empty() |
如果 v 为空,则返回 true, 否则返回 false 。 |
v . size () |
返回 v 中元素的个数。 |
v . push _ back ( t ) |
在 v 的末尾增加一个值为 t 的元素。 |
v [ n ] |
返回 v 中位置为 n 的元素。 |
v1 = v2 |
把 v1 的元素替换为 v2 中元素的副本。 |
v1 == v2 |
如果 v1 与 v2 相等,则返回 true 。 |
!=, <, <=, >, >= |
保持这些操作符惯有的含义。 |
vector 对象的 size
empty 和 size 操作类似于 string 类型的相关操作。成员函数 size 返回相应 vector 类定义的size_type 的值。
使用 size_type 类型时,必须指出该类型是在哪里定义的。 vector 类型总是 包括 vector 的元素类型:
vector<int>::size_type // ok
vector::size_type // error
向 vector 添加元素
push_back() 操作接受一个元素值,并将它作为一个新的元素添加到 vector 对象的后面,也就是“ 插入 ( push)” 到 vector 对象的 “ 后面 ( back ) ” :
- <span style="font-weight:normal;"><span style="font-size:14px;">// read words from the standard input and store them as elements in a vector
- string word;
- vector<string> text; // empty vector
- while (cin >> word) {
- text.push_back(word); // append word to text
- }</span></span>
该循环从标准输入读取一系列 string 对象,逐一追加到 vector 对象的后面。首先定义一个空的 vector 对象 text 。每循环一次就添加一个新元素到 vector 对象,并将从 输入 读取的 word 值赋予该元素。当循环结束时, text 就包含了所有读入的元素。
vector 的下标操作(更推荐用迭代器)
vector 中的对象是没有命名的,可以按 vector 中对象的位置来访问它们。通常使用下标操作符来获取元素。 vector的下标操作类似于 string 类型的下标操作 ( 3 .2 .3 节 ) 。
vector 的下标操作符接受一个值,并返回 vector 中该对应位置的元素。 vector 元素的位置从 0 开始。下例使用 for循环把 vector 中的每个元素值都重置为 0 :
- <span style="font-weight:normal;"><span style="font-size:14px;">// reset the elements in the vector to zero
- for (vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
- ivec[ix] = 0;</span></span>
和 string 类型的下标操作符一样, vector 下标操作的结果为左值,因此可以像循环体中所做的那样实现写入。另外,和 string 对象的下标操作类似,这里用 size_type 类型作为 vector 下标的类型。
在上例中,即使 ivec 为空, for 循环也会正确执行。 ivec 为空则调用 size 返回 0 ,并且 for 中的测试比较 ix 和 0 。第一次循环时,由于 ix 本身就是 0 ,则条件测试失败, for 循环体一次也不执行。仅能对确知已存在的元素进行下标操作
初学 C ++ 的程序员可能会认为 vector 的下标操作可以添加元素,其实不然:
- vector<int> ivec; // empty vector
- for (vector<int>::size_type ix = 0; ix != 10; ++ix)
- ivec[ix] = ix; <span style="color:#ff0000;"><strong>// disaster: ivec has no elements</strong></span>
上述程序试图在 ivec 中插入 10 个新元素,元素值依次为 0 到 9 的整数。但是,这里 ivec 是空的 vector 对象,而且下标只能用于获取已存在的元素。
这个循环的正确写法应该是:
- for (vector<int>::size_type ix = 0; ix != 10; ++ix)
- ivec.push_back(ix); // ok: adds new element with value ix<span style="color:#000000;font-family:Arial;"> </span>
必须是已存在的元素才能用下标操作符进行索引。通过下标操作进行赋值时,不会添加任何元素。
对于下标操作符 ( [] 操作符 ) 的使用有一点非常重要,就是仅能提取确实已存在的元素,例如:
- 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
试图获取不存在的元素必然产生运行时错误。和大多数同类错误一样,不能确保执行过程可以捕捉到这类错误,运行程序的结果是不确定的。由于取不存在的元素的结果是未定义的,因而不同的实现会导致不同的结果,但程序运行时几乎肯定会以某种有趣的方式失败。
本警告适用于任何使用下标操作的时候,如 string 类型的下标操作,以及将要简要介绍的内置数组的下标操作。
不幸的是,试图对不存在的元素进行下标操作是程序设计过程中经常会犯的严重错误。所谓的“缓冲区溢出”错误就是对不存在的元素进行下标操作的结果。这样的缺陷往往导致 PC 机和其他应用中最常见的安全问题。
其他以及迭代器
① v.resize(2*v.size)或v.resize(2*v.size, 99) 将v的容量翻倍(并把新元素的值初始化为99)
②使用迭代器访问元素.
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
cout<<*it<<endl;
- c.insert(pos,elem) // 在pos位置插入一个elem拷贝,传回新数据位置。
- c.insert(pos,n,elem) // 在pos位置插入n个elem数据。无返回值。
- c.insert(pos,beg,end) // 在pos位置插入在[beg,end)区间的数据。无返回值。
④
c.erase(pos) 删除pos位置的数据,传回下一个数据的位置。
c.erase(beg,end) 删除[beg,end)区间的数据,传回下一个数据的位置。
⑤
c.assign(beg,end) 将[beg; end)区间中的数据赋值给c
c.assign(n,elem) 将n个elem的拷贝赋值给c。
⑥ c.at(idx) 传回索引idx所指的数据,如果idx越界,抛出out_of_range。
⑦c.back() 传回最后一个数据,不检查这个数据是否存在。
⑧ c.begin()传回迭代器中的第一个数据
c.clear()移除容器中所有数据。
c.empty()判断容器是否为空。
c.end()指向迭代器中末端元素的下一个,指向一个不存在元素。
其他更多参见百科:http://baike.baidu.com/link?url=L6vhhZKTvp6C62nPFPbYKnxbqXnRUTwt-2dV7e07ACU0CnFJ6kbyDbwQ-iPGqoY_More:vector结构体
vector的元素不仅仅可以使int,double,string,还可以是结构体,但是要注意:结构体要定义为全局的,否则会出错。下面是一段简短的程序代码:
- #include<stdio.h>
- #include<algorithm>
- #include<vector>
- #include<iostream>
- using namespace std;
- typedef struct rect
- {
- int id;
- int length;
- int width;
- //对于向量元素是结构体的,可在结构体内部定义比较函数,下面按照id,length,width升序排序。
- bool operator< (const rect &a) const
- {
- if(id!=a.id)
- return id<a.id;
- else
- {
- if(length!=a.length)
- return length<a.length;
- else
- return width<a.width;
- }
- }
- }Rect;
- int main()
- {
- vector<Rect> vec;
- Rect rect;
- rect.id=1;
- rect.length=2;
- rect.width=3;
- vec.push_back(rect);
- vector<Rect>::iterator it=vec.begin();
- cout<<(*it).id<<‘ ‘<<(*it).length<<‘ ‘<<(*it).width<<endl;
- return 0;
- }
算法:
#include<algorithm>中的泛函算法搜索算法:find() 、search() 、count() 、find_if() 、search_if() 、count_if()
分类排序:sort() 、merge()
删除算法:unique() 、remove()
生成和变异:generate() 、fill() 、transformation() 、copy()
关系算法:equal() 、min() 、max()
sort(v1.begin(),vi.begin()+v1.size/2); 对v1的前半段元素排序
list<char>::iterator pMiddle =find(cList.begin(),cList.end(),‘A‘);找到则返回被查内容第一次出现处指针,否则返回end()。
vector< typeName >::size_type x ; vector< typeName >类型的计数,可用于循环如同for(int i)
使用reverse将元素翻转
reverse(vec.begin(),vec.end());将元素翻转(在vector中,如果一个函数中需要两个迭代器,
一般后一个都不包含.)
使用sort排序
sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大).
可以通过重写排序比较函数按照降序比较,如下:
定义排序比较函数:
- bool Comp(const int &a,const int &b)
- {
- return a>b;
- }
- /*
- vector 综合练习
- Written by C_SuooL_Hu
- 2013 10 29
- */
- #include <vector>
- #include <string>
- #include <iostream>
- #include <algorithm>
- using namespace std ;
- int main()
- {
- // 四种初始化方式
- vector<int> ivec_1;
- vector<int> ivec_2(ivec_1);
- vector<int> ivec_3(10, 0) ;
- vector<int> ivec(10);
- // 定义迭代器变量
- vector<int>::iterator iter;
- // 定义下标变量
- vector<int>::size_type ix ;
- system("echo off");
- system("color 3") ;
- // reset all the elements in ivec to 0
- // 使用下标操作,重置为1
- for (ix = 0; ix != ivec.size(); ++ix)
- ivec[ix] = 1;
- // print what we‘ve got so far: should print 10 0‘s
- cout << "使用下标赋值为1的vector<int>类型(同样用下标遍历输出):"<< endl;
- for (ix = 0; ix != ivec.size(); ++ix)
- cout << ivec[ix] << " ";
- cout << endl;
- // 使用迭代器的操作赋值,重置为0
- for (iter = ivec.begin(); iter !=ivec.end(); ++iter )
- *iter = 0;
- // 遍历输出,使用迭代器
- cout<<"使用迭代器赋值为0的vector<int>类型(同样用迭代器遍历输出):" << endl;
- for (iter = ivec.begin(); iter !=ivec.end(); ++iter )
- cout<< *iter << ‘ ‘ ;
- cout<< endl ;
- iter = ivec.begin();
- while (iter != ivec.end())
- {
- *iter =2 ;
- ++iter;
- }
- // 遍历输出,使用迭代器
- cout<<"使用while循环的迭代器赋值为2的数列的vector<int>类型(同样用迭代器遍历输出):" << endl;
- for (iter = ivec.begin(); iter !=ivec.end(); ++iter )
- cout<< *iter << ‘ ‘ ;
- cout<< endl << endl ;
- // 使用vector的成员函数操作vector<int>类型数据
- // 添加元素
- cout << "使用vector的成员函数操作vector<int>类型数据" << endl;
- cout << "添加元素" << endl ;
- cout << "使用迭代器,添加九个递增元素" << endl ;
- for (ix = 0; ix != 10; ++ ix)
- ivec.push_back(ix);
- cout << "此时ivec共有" << ivec.size() << "个元素"<< endl;
- // 遍历输出,使用迭代器
- cout<<"使用下标增加元素为递增的的数列的vector<int>类型(同样用迭代器遍历输出):" << endl;
- for (iter = ivec.begin(); iter !=ivec.end(); ++iter )
- cout<< *iter << ‘ ‘ ;
- cout<< endl << endl;
- // 插入元素
- cout << "插入元素:在第二个位置插入一个43" << endl ;
- ivec.insert(ivec.begin() + 1, 43); // 在2位置插入一个43拷贝,传回新数据位置。
- // 遍历输出,使用迭代器
- cout<<"使用迭代器遍历输出:" << endl;
- for (iter = ivec.begin(); iter !=ivec.end(); ++iter )
- cout<< *iter << ‘ ‘ ;
- cout << "\n此时ivec共有" << ivec.size() << "个元素" ;
- cout<< endl << endl ;
- cout << "插入元素:在第一个位置插入三个13" << endl ;
- ivec.insert(ivec.begin(), 3 , 13); // 在1位置插入3个13数据。无返回值。
- // 遍历输出,使用迭代器
- cout<<"使用迭代器遍历输出:" << endl;
- for (iter = ivec.begin(); iter !=ivec.end(); ++iter )
- cout<< *iter << ‘ ‘ ;
- cout << "\n此时ivec共有" << ivec.size() << "个元素";
- cout<< endl << endl ;
- cout << "插入元素:在第7个位置插入ivec的[1, 6) 之间的数据:" << endl ;
- ivec.insert(ivec.begin()+6, ivec.begin(), ivec.begin()+5); // 在7位置插入在[10,19)区间的数据。无返回
- // 遍历输出,使用迭代器
- cout<<"使用迭代器遍历输出:" << endl;
- for (iter = ivec.begin(); iter !=ivec.end(); ++iter )
- cout<< *iter << ‘ ‘ ;
- cout << "\n此时ivec共有" << ivec.size() << "个元素";
- cout<< endl << endl ;
- cout << "使用assign赋值ivec_1(将ivec的第[10,19)个元素赋值给他):" <<endl ;
- ivec_1.assign(ivec.begin()+9, ivec.begin()+18);
- cout<<"使用迭代器遍历输出:" << endl;
- for (iter = ivec_1.begin(); iter !=ivec_1.end(); ++iter )
- cout<< *iter << ‘ ‘ ;
- cout << "\n此时ivec_1共有" << ivec_1.size() << "个元素";
- cout << endl;
- cout << "使用assign赋值ivec_2:" <<endl ;
- ivec_2.assign(10,8) ;
- cout<<"使用迭代器遍历输出:" << endl;
- for (iter = ivec_2.begin(); iter !=ivec_2.end(); ++iter )
- cout<< *iter << ‘ ‘ ;
- cout << "\n此时ivec_2共有" << ivec_2.size() << "个元素";
- cout << endl;
- // 元素排序
- /*
- 由于vc 6.0对模板库支持不够好,这里的排序函数无法通过编译,但是使用vs2010编译通过。
- cout << "对ivec排序:" << endl;
- bool Comp(const int &a,const int &b)
- {
- return a>b;
- }
- sort (ivec.begin(), ivec.end,Comp);
- cout<<"从大到小排序后,使用迭代器遍历输出:" << endl;
- for (iter = ivec_2.begin(); iter !=ivec_2.end(); ++iter )
- cout<< *iter << ‘ ‘ ;
- */
- cout << "是否为空(0代表不是,1代表是):" << ivec.empty() << endl;
- ivec.clear() ;
- cout << "清空元素" << endl;
- cout << "是否为空:" << ivec.empty() << endl;
- return 0;
- }
运行结果: