72.STL中的vector

72.STL中的vector

1.vector概述

vector是最常用的容器之一,功能十分强大,可以储存、管理各种类型的数据。在很多情况下可以用来代替功能比较局限的普通数组,因为我们知道,普通数组只能实现一对一的映射而不能实现一对多的映射,vector就是专门为了解决这个问题而诞生的。vector也可以称为动态数组,因为其大小是根据实时更新而变化的,正因为如此vector显得更加灵活易用。

vector的数据安排以及操作方式,与array非常相似,两者的唯一差别在于空间的运用的灵活性。Array是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎得由自己来,首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。Vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此vector的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必害怕空间不足而一开始就要求一个大块头的array了。

Vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率,一旦vector旧空间满了,如果客户每新增一个元素,vector内部只是扩充一个元素的空间,实为不智,因为所谓的扩充空间(不论多大),一如刚所说,是”配置新空间-数据移动-释放旧空间”的大工程,时间成本很高,应该加入某种未雨绸缪的考虑,稍后我们便可以看到vector的空间配置策略。

C++语言既有类模板(class template),也有函数模板,其中vector是个类模板。

模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的份说明。编译器根据模板创建类或函数的过程称为实例化(instantiation),当使用模板时,需要指出编译器应把类或函数实例化成何种类型。

NOTE:vector是模板而非类型,由vector,生成的类型必须包含vector中元素的类型,例如vector<int>。

vector能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的vector。除此之外,其他大多数(非引用)内置类型和类类型都可以构成vector对象,甚至组成vector的元素也可以是vector。

2.vector的定义和初始化

使用vector需要包含vector头文件如下:

#include<vector>
using std::vector;

和任何种类类型一样,vector模板控制着定义和初始化向量的方法。表3.4列出了vector对象的常用方法

表3.4: 初始化vector对象的方法
vector<T> v1 v1是一个空vector,它潜在的元素是T类型的,执行默认初始化
vector<T> v2(v1) v2中包含有v1所有元素的副本
vector<T> v2 = v1 等价于v2(v1), v2中包含有v1所有元素的副本
vector<T> v3(n, val) v3包含了n个重复的元素,每个元素的值都是val
vector<T> v4(n) v4包含了n个重复地执行了值初始化的对象
vector<T> v5{a,b,c...} v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<T> v5={a,b,c...} 等价于v5{a,b,c...}

例子:

vector<int> v1;//声明一个int型向量v1,默认初始化为0
vector<int> v2(v1);//声明并用向量v1初始化向量v2,v2是包含有v1所有元素的副本
vector<int> v2 = v1;//等价于v2(v1),声明并用向量v1初始化向量v2,v2是包含有v1所有元素的副本
vector<int> v3(10, 1);//向量v3包含了10个重复的元素,每个元素都是1
vector<int> v4(10);//向量v1包含了10个重复地执行了值初始化的对象
vector<int> v5{a, b, c,..};//向量v1包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<int> v5={ a, b, c,.. };//等价于v5{a, b, c,..};
vector<int> v6(a.begin(), a.begin() + 3);//将v6向量中从第0个到第2个(共3个)作为向量b的初始值
vector<string> v7 = { "hi","my","name","is","lee" };

int arr[] = { 0,1,2,3,4,5,6 };
vector<int>v8 = { 0,1,2,3,4 };//列表初始化
vector<int>v9(arr, arr + 3);//将arr数组的前是三个元素作为向量v9的初始值,区间为[arr,arr+3)
vector<int>v10(&arr[1], &arr[4]);//将arr[1]~arr[4]之间的元素作为向量v8的初始值

(1)值初始化

通常情况下,可以只提供vector对象容纳的元素数量而不用略去初始值。此时库会创建一个值初始化的(value-initialized)元素初值,并把它赋给容器中的所有元素。这个初值由vector对象中元素的类型决定。
如果vector对象的元素是内置类型,比如int,则元素初始值自动设为0。如果元素是某种类类型,比如string,则元素由类默认初始化 :

vector<int> ivec(10);//10个元素,每个都初始化为0
vector<string> svec(10);//10个元素,每个都是空string

对这种初始化的方式有两个特殊限制:其一,有些类要求必须明确地提供初始值,如果vector对象中元素的类型不支持默认初始化,我们就必须提供初始的元素值。对这种类型的对象来说,只提供元素的数量而不设定初始值无法完成初始化工作。

其二,如果只提供了元素的数量而没有设定初始值,只能使用直接初始化:

vector<int> vi= 10;//错误:必须使用直接初始化的形式指定向量大小

这里的10是用来说明如何初始化vector对象的,我们用它的本意是想创建含有10个值初始化了的元素的vector对象,而非把数字10 “拷贝” 到vector中。因此,此时不宜使用拷贝初始化。

3.vector迭代器

迭代器 功能
begin() 返回指向向量第一个元素的迭代器
end() 返回指向向量最后一个元素之后位置的迭代器
rbegin() 返回指向向量最后一个元素的反向迭代器
rend() 返回指向向量第一个元素之前位置的反向迭代器
cbegin() 返回指向向量第一个元素的迭代器,不能用于修改元素
cend() 返回指向向量最后一个元素之后位置的迭代器,不能用于修改元素
crbegin() 返回指向向量最后一个元素的反向迭代器,不能用于修改元素
crend() 返回指向向量第一个元素之前位置的反向迭代器,不能用于修改元素

4.向vector对象中添加元素

(1)a.assign(b.begin(), b.begin()+3); //b为向量,将b的0~2个元素构成的向量赋给a
(2)a.assign(4,2); //是a只含4个元素,且每个元素为2
(3)a.back(); //返回a的最后一个元素
(4)a.front(); //返回a的第一个元素
(5)a[i]; //返回a的第i个元素,当且仅当a[i]存在2013-12-07
(6)a.clear(); //清空a中的元素
(7)a.empty(); //判断a是否为空,空则返回ture,不空则返回false
(8)a.pop_back(); //删除a向量的最后一个元素
(9)a.erase(a.begin()+1,a.begin()+3); //删除a中第1个(从第0个算起)到第2个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+         3(不包括它)
(10)a.push_back(5); //在a的最后一个向量后插入一个元素,其值为5
(11)a.insert(a.begin()+1,5); //在a的第1个元素(从第0个算起)的位置插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4
(12)a.insert(a.begin()+1,3,5); //在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
(13)a.insert(a.begin()+1,b+3,b+6); //b为数组,在a的第1个元素(从第0个算起)的位置插入b的第3个元素到第5个元素(不包括b+6),如b为1,2,3,4,5,9,8         ,插入元素后为1,4,5,9,2,3,4,5,9,8
(14)a.size(); //返回a中元素的个数;
(15)a.capacity(); //返回a在内存中总共可以容纳的元素个数
(16)a.resize(10); //将a的现有元素个数调至10个,多则删,少则补,其值随机
(17)a.resize(10,2); //将a的现有元素个数调至10个,多则删,少则补,其值为2
(18)a.reserve(100); //将a的容量(capacity)扩充至100,也就是说现在测试a.capacity();的时候返回值是100.这种操作只有在需要给a添加大量数据的时候才         显得有意义,因为这将避免内存多次容量扩充操作(当a的容量不足时电脑会自动扩容,当然这必然降低性能) 
(19)a.swap(b); //b为向量,将a中的元素和b中的元素进行整体性交换
(20)a==b; //b为向量,向量的比较操作还有!=,>=,<=,>,<

对vector对象来说,直接初始化的方式适用于三种情况:初始值已知且数量较少、初始值是另一个vector对象的副本、所有元素的初始值都一样。然而更常见的情况是:创建一个vector对象时并不清楚实际所需的元素个数,元素的值也经常无法确定。还有 些时候即使元素的初值已知,但如果这些值总量较大而各不相同,那么在创建vector对象的时候执行初始化操作也会显得过于烦琐。

举个例子,如果想创建一个vector对象令其包含从0到9共10个元素,使用列表初始化的方法很容易做到这一点:但如果vector对象包含的元素是从0到99或者从0 到999呢?这时通过列表初始化把所有元素都一一罗列出来就不太合适了。对于此例来说,更好的处理方法是先创建一个空vector,然后在运行时再利用vector的成员函数 push_back向其中添加元素。push_back负责把一个值当成vector对象的尾元素“压到(push)”vector对象的“尾端(back) ”。例如:

vector<int> vec;
for (int i = 0; i != 100; i++)
{
    vec.push_back(i);
}    
// 循环结束后vec中100个元素,值从0到99            

同样的,如果直到运行时才能知道vector对象中元素的确切个数,也应该使用刚刚这种方法创建 vector对象并为其赋值。例如,有时需要实时读入数据然后将其赋予vector对象:

//从标准输入中读取单词,将其作为vector对象的元素存储
string word;
vector<string> text; //空vector对象
while (cin >> word)
{
	text.push_back(word);//把word添加到text后面
}

和之前的例子一样,本例也是先创建一个空vector,之后依次读入未知数量的值并保存到test中。

关键概念:vector对象能高效增长

C++标准要求vector应该能在运行时高效快速地添加元素。因此既然vector对 象能高效地增长,那么在定义vector对象的时候设定其大小也就没什么必要了,事实上如果这么做性能可能更差。只有一种例外情况,就是所有 (all) 元素的值都一样。一旦元素的值有所不同,更有效的办法是先定义一个空的vector对象,再在运行时向其中添加具体值。此外,允许我们进一步提升动态添加元素的性能。

开始的时候创建空的vector对象在运行时再动态添加元素,这一做法与C语言及其他大多数语言中内置数组类型的用法不同。特别是如果用惯了C或者Java,可以预计在创建vector对象时顺便指定其容量是最好的。然而事实上,通常的情况是恰恰相反。

(1)向vector对象添加元素蕴含的编程假定

由于能高效便捷地向vector对象中添加元素,很多编程工作被极大简化了。然而,这种简便性也伴随着一些对编写程序更高的要求:其中一条就是必须要确保所写的循环正确无误,特别是在循环有可能改变vector对象容量的时候。

随着对vector的更多使用,我们还会逐渐了解到其他一些隐含的要求,其中一条是现在就要指出的:如果循环体内部包含有向vector对象添加元素的语句,则不能使用范围for循环,因为在范围for语句中,预存了尾部位置相关的标记位置的值,增减元素会改变其值。

5.vector类常用的函数

5.1构造函数

vector():创建一个空vector
vector(int nSize):创建一个vector,元素个数为nSize
vector(int nSize,const T& t):创建一个vector,元素个数为nSize,且值均为t
vector(const vector&):复制构造函数
vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中

例1:

//#include "stdafx.h"  
#include<iostream>  
#include<vector>  
using namespace std;

class A
{
    //空类  
};

int main(int argc, char* argv[])
{
    //int型vector  
    vector<int> vecInt;

    //float型vector  
    vector<float> vecFloat;

    //自定义类型,保存类A的vector  
    vector<A> vecA;

    //自定义类型,保存指向类A的指针的vector  
    vector<A*> vecPointA;

    return 0;
}

例2:

// vectorsample.cpp : 定义控制台应用程序的入口点。  
//  
//#include "stdafx.h"  
#include<iostream>  
#include<vector>  
using namespace std;

class A
{
    //空类  
};

int main(int argc, char* argv[])
{
    //int型vector,包含3个元素  
    vector<int> vecIntA(3);

    //int型vector,包含3个元素且每个元素都是9  
    vector<int> vecIntB(3, 9);

    //复制vecIntB到vecIntC  
    vector<int> vecIntC(vecIntB);

    int iArray[] = { 2,4,6 };
    //创建vecIntD  
    vector<int> vecIntD(iArray, iArray + 3);

    //打印vectorA,此处也可以用下面注释内的代码来输出vector中的数据  
    /*for(int i=0;i<vecIntA.size();i++)
    {
        cout<<vecIntA[i]<<"     ";
    }*/

    cout << "vecIntA:" << endl;
    for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
    {
        cout << *it << "     ";
    }
    cout << endl;

    //打印vecIntB  
    cout << "VecIntB:" << endl;
    for (vector<int>::iterator it = vecIntB.begin(); it != vecIntB.end(); it++)
    {
        cout << *it << "     ";
    }
    cout << endl;

    //打印vecIntC  
    cout << "VecIntB:" << endl;
    for (vector<int>::iterator it = vecIntC.begin(); it != vecIntC.end(); it++)
    {
        cout << *it << "     ";
    }
    cout << endl;

    //打印vecIntD  
    cout << "vecIntD:" << endl;
    for (vector<int>::iterator it = vecIntD.begin(); it != vecIntD.end(); it++)
    {
        cout << *it << "     ";
    }
    cout << endl;

    return 0;
}

输出:

vecIntA:
0     0     0
VecIntB:
9     9     9
VecIntB:
9     9     9
vecIntD:
2     4     6

例3:

#include<iostream>  
#include<vector>  
using namespace std;

void printVector(vector<int>& v)
{	//利用迭代器打印 v
    for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void text01()
{
    vector<int> v1;                       
    for (int i = 0; i < 5; ++i)
    {
        v1.push_back(i);//向v1末尾添加数据
    }
    vector<int> v2(v1.begin(), v1.end());
    vector<int> v3(5, 5);                
    vector<int> v4(v3);                  
    cout << "打印v2: ";
    printVector(v2);
    cout << "打印v3: ";
    printVector(v3);
    cout << "打印v4: ";
    printVector(v4);
}

int main(int argc, char* argv[])
{
    text01();

    return 0;
}

输出:

打印v2: 0 1 2 3 4
打印v3: 5 5 5 5 5
打印v4: 5 5 5 5 5

5.2增加函数

void push_back(const T& x):向量尾部增加一个元素X
iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据

例子:

// vectorsample.cpp : 定义控制台应用程序的入口点。  
// vectorsample.cpp : 定义控制台应用程序的入口点。 
//#include "stdafx.h"  
#include<iostream>  
#include<vector>  

using namespace std;


int main(int argc, char* argv[])
{
    //int型vector,包含3个元素  
    vector<int> vecIntA;

    //插入1 2 3  
    vecIntA.push_back(1);
    vecIntA.push_back(2);
    vecIntA.push_back(3);

    int nSize = vecIntA.size();

    cout << "vecIntA:" << endl;

    //打印vectorA,方法一:  
    for (int i = 0; i < nSize; i++)
    {
        cout << vecIntA[i] << "     ";
    }
    cout << endl;

    //打印vectorA,方法二:      
    for (int i = 0; i < nSize; i++)
    {
        cout << vecIntA.at(i) << "     ";
    }
    cout << endl;

    //打印vectorA,方法三:  
    for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
    {
        cout << *it << "     ";
    }
    cout << endl;

    return 0;
}

上述代码对一个整形向量类进行操作,先定义一个整形元素向量类,然后插入3个值,并用3种不同的方法输出,程序运行结果如下:

vecIntA:
1     2     3
1     2     3
1     2     3

例子2:

// vectorsample.cpp : 定义控制台应用程序的入口点。  
//  
//#include "stdafx.h"  
#include<iostream>  
#include<vector>  
using namespace std;

class A
{
public:
    int n;
public:
    A(int n)
    {
        this->n = n;
    }
};

int main(int argc, char* argv[])
{
    //int型vector,包含3个元素  
    vector<A> vecClassA;

    A a1(1);
    A a2(2);
    A a3(3);

    //插入1 2 3  
    vecClassA.push_back(a1);
    vecClassA.push_back(a2);
    vecClassA.push_back(a3);


    int nSize = vecClassA.size();

    cout << "vecClassA:" << endl;

    //打印vecClassA,方法一:  
    for (int i = 0; i < nSize; i++)
    {
        cout << vecClassA[i].n << "     ";
    }
    cout << endl;

    //打印vecClassA,方法二:    
    for (int i = 0; i < nSize; i++)
    {
        cout << vecClassA.at(i).n << "     ";
    }
    cout << endl;

    //打印vecClassA,方法三:  
    for (vector<A>::iterator it = vecClassA.begin(); it != vecClassA.end(); it++)
    {
        cout << (*it).n << "     ";
    }
    cout << endl;

    return 0;
}

上述代码通过定义元素为类的向量,通过插入3个初始化的类,并通过3种方法输出,运行结果如下:

vecClassA:
1     2     3
1     2     3
1     2     3

上述代码通过定义元素为类指针的向量,通过插入3个初始化的类指针,并通过3种方法输出指针指向的类,运行结果如下:

vecClassA:
1       2       3
1       2       3
1       2       3

5.3删除函数

iterator erase(iterator it) :删除向量中迭代器指向元素
iterator erase(iterator first, iterator last) : 删除向量中[first, last)中元素
void pop_back() :删除向量中最后一个元素
void clear() : 清空向量中所有元素

例1:

// vectorsample.cpp : 定义控制台应用程序的入口点。  
//#include "stdafx.h"  
#include<iostream>  
#include<vector>  
using namespace std;

int main(int argc, char* argv[])
{
    //int型vector,包含3个元素  
    vector<int> vecIntA;

    //循环插入1 到10  
    for (int i = 1; i <= 10; i++)
    {
        vecIntA.push_back(i);
    }

    vecIntA.erase(vecIntA.begin() + 4);

    cout << "删除第5个元素后的向量vecIntA:" << endl;
    //打印vectorA  
    for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
    {
        cout << *it << "\t";
    }
    cout << endl;

    //删除第2-5个元素  
    vecIntA.erase(vecIntA.begin() + 1, vecIntA.begin() + 5);

    cout << "删除第2-5个元素后的vecIntA:" << endl;
    //打印vectorA  
    for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
    {
        cout << *it << "\t";
    }
    cout << endl;

    //删除最后一个元素  
    vecIntA.pop_back();

    cout << "删除最后一个元素后的vecIntA:" << endl;
    //打印vectorA  
    for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
    {
        cout << *it << "\t";
    }
    cout << endl;

    return 0;
}

程序运行结果如下:

删除第5个元素后的向量vecIntA:
1       2       3       4       6       7       8       9       10
删除第2-5个元素后的vecIntA:
1       7       8       9       10
删除最后一个元素后的vecIntA:
1       7       8       9

5.4遍历函数

reference at(int pos) :返回pos位置元素的引用
reference front() : 返回首元素的引用
reference back() : 返回尾元素的引用
iterator begin() : 返回向量头指针,指向第一个元素
iterator end() : 返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin() : 反向迭代器,指向最后一个元素
reverse_iterator rend() : 反向迭代器,指向第一个元素之前的位置

5.5判空函数

bool empty() const:判断向量是否为空,若为空,则向量中无元素

5.6大小函数

int size() const:返回向量中元素的个数
int capacity() const:返回当前向量所能容纳的最大元素值
int max_size() const:返回最大可允许的vector元素数量值

5.7其他函数

void swap(vector&):交换两个同类型向量的数据
void assign(int n,const T& x):设置向量中第n个元素的值为x
void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素
resize(num)重新指定队列的长度。(往往用来增加vector的长度,小->大 ok 大->小 没用!)
reserve()保留适当的容量。
front() 返回指向向量第一个元素的引用
back()返回指向向量最后一个元素的引用

1.assign函数介绍

函数原型:

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

void assign( std::initializer_list<T> ilist );//(C++11 起) (C++20 前)
constexpr void assign( std::initializer_list<T> ilist );//(C++20 起)
void assign(const_iterator first, const_iterator last);

功能:将区间[first,last)的元素赋值到当前的vector容器中,或者赋n个值为x的元素到vector容器中,这个容器会清除掉vector容器中以前的内容。

例1:

// vector assign
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
    std::vector<double> first{ 1.9, 2.9, 3.9, 4.9, 5.9 }; /*初始化源数组*/
    std::vector<double> second;                           /*声明空数组*/
    std::vector<int> third;
    std::vector<string> forth;

    std::vector<double>::iterator it;
    it = first.begin();

    second.assign(it, first.end());
    std::cout << "Size of second: " << int(second.size()) << '\n';
    for (int i = 0; i < second.size(); i++)
        cout << second[i] << " ";
    cout << endl;
    //结果:
    //Size of second: 5
    //1.9 2.9 3.9 4.9 5.9


    second.assign(it + 1, first.end() - 1);
    std::cout << "Size of second: " << int(second.size()) << '\n';
    for (int i = 0; i < second.size(); i++)
        cout << second[i] << " ";
    cout << endl;
    //Size of second: 3
    //2.9 3.9 4.9


    third.assign(it, first.end());
    std::cout << "Size of third: " << int(third.size()) << '\n';
    for (int i = 0; i < third.size(); i++)
        cout << third[i] << " ";
    cout << endl;
    //Size of third: 5
    //1 2 3 4 5

    int myints[] = { 1776,7,4 };
    third.assign(myints, myints + 3);  /* assign with array*/
    std::cout << "Size of third: " << int(third.size()) << '\n';
    for (int i = 0; i < third.size(); i++)
        cout << third[i] << " ";
    cout << endl;
    //Size of third: 3
    //1776 7 4

    //third.assign (myints,myints+4); /*error usage,有结果但是行为错误*/
    //1776 7 4 787800
    // third = first; /*error usage*/
    // forth.assign(it,first.end()); /*error usage*/
    return 0;
}

输出:

Size of second: 5
1.9 2.9 3.9 4.9 5.9
Size of second: 3
2.9 3.9 4.9
Size of third: 5
1 2 3 4 5
Size of third: 3
1776 7 4
void assign(size_type n, const T& x = T());

功能:将调用者中内容替换成n个x

例2:

// vector assign
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
    vector<double> first{ 1.9, 2.9, 3.9, 4.9, 5.9 }; /*初始化源数组*/
    cout << "Size of first: " << int(first.size()) << '\n';
    for (int i = 0; i < first.size(); i++)
        cout << first[i] << " ";
    cout << endl;

    first.assign(10, 5);
    cout << "Size of first: " << int(first.size()) << '\n';
    for (int i = 0; i < first.size(); i++)
        cout << first[i] << " ";
    cout << endl;

    return 0;
}

输出:

Size of second: 5
1.9 2.9 3.9 4.9 5.9
Size of second: 10
5 5 5 5 5 5 5 5 5 5
void assign( std::initializer_list<T> ilist );//(C++11 起) (C++20 前)
constexpr void assign( std::initializer_list<T> ilist );//(C++20 起)

功能:将当前vector内容替换成ilist中内容

// vector assign
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
    vector<double> first{ 1.9, 2.9, 3.9, 4.9, 5.9 }; /*初始化源数组*/
    vector<double> second{ 1, 2, 3, 4, 5 }; /*初始化源数组*/

    cout << "Size of first: " << int(first.size()) << '\n';
    for (int i = 0; i < first.size(); i++)
        cout << first[i] << " ";
    cout << endl;

    second.assign({1, 2, 3});
    cout << "Size of second: " << int(second.size()) << '\n';
    for (int i = 0; i < second.size(); i++)
        cout << second[i] << " ";
    cout << endl;

    return 0;
}

输出:

Size of first: 5
1.9 2.9 3.9 4.9 5.9
Size of second: 3
1 2 3

2.reserve和resize

vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector的capacity同时也增加了它的size!

原因如下:
reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素。加入新的元素时,要调用push_back()/insert()函数。

resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。此时再调用push_back()函数,是加在这个新的空间后面的。

两个函数的参数形式也有区别的,reserve函数之后一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小, 第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。

(1)resize用法:

void resize(size_type new_size);  
void resize(size_type new_size, const value_type& value);
void resize(size_type new_size); 

①new_size>size()时,如果new_size>capacity(),那么size()和capacity()大小同时变为new_size,重新分配出来的空间执行默认初始化。如果size()<new_size<capacity(),size()大小变为new_size、capacity()不变,size()和new_size之间的数据执行默认初始化,new_size和capacity()之间的数据无法访问。

②new_size<size()时,size()大小变为new_size,capacity()不变,new_size和capacity()之间数据无法访问

例1:

#include <iostream>  
#include <vector>  
using namespace std;

int main() 
{
    std::vector<int> vec(5); // 创建一个大小为5的向量  
    for (int i = 0; i < vec.size(); i++) 
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    
    for (int i = 0; i < vec.size(); i++)
    {
        vec[i] = 2*i;
        cout << vec[i] << " ";
    }
    
    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    vec.resize(7); // 改变向量的大小为7  
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec[i] << " ";
    }

    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    vec.resize(5); // 改变向量的大小为5  
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec[i] << " ";
    }

    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    return 0;
}

输出:

0 0 0 0 0
0 2 4 6 8
vec.size():5, vec.capacity():5
===========================================================
0 2 4 6 8 0 0
vec.size():7, vec.capacity():7
===========================================================
0 2 4 6 8
vec.size():5, vec.capacity():7
===========================================================
void resize(size_type new_size, const value_type& value);

①new_size<size()时,结果是容器的size()减小到new_size,删除n之后的元素,capacity()不变

②size()<new_size<capacity()时,结果是容器的size()增加到new_size,增加的元素值初始化为value,capacity()不变

③new_size>capacity()时,结果是容器的size()和capacity()增加到new_size,增加的元素值初始化为value

例2:

#include <iostream>  
#include <vector>  
using namespace std;

int main() 
{
    std::vector<int> vec(5); // 创建一个大小为5的向量  
    for (int i = 0; i < vec.size(); i++) 
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    for (int i = 0; i < vec.size(); i++)
    {
        vec[i] = 2*i;
        cout << vec[i] << " ";
    }
    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    vec.resize(3,6); // 改变向量的大小为3
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    vec.resize(4, 6); // 改变向量的大小为3
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    vec.resize(7, 6); // 改变向量的大小为3
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    return 0;
}

输出:

0 0 0 0 0
0 2 4 6 8
vec.size():5, vec.capacity():5
===========================================================
0 2 4
vec.size():3, vec.capacity():5
===========================================================
0 2 4 6
vec.size():4, vec.capacity():5
===========================================================
0 2 4 6 6 6 6
vec.size():7, vec.capacity():7
===========================================================

(2)reserve用法:

void reserve( size_type new_cap );//(C++20 前)
constexpr void reserve( size_type new_cap );//(C++20 起)

①new_cap < capacity()时,size与capacity均不发生改变;

②new_cap > capacity()时,此时会重新分配一块空间,使得capacity扩容至n,size不发生改变。

#include <iostream>  
#include <vector>  
using namespace std;

int main() 
{
    std::vector<int> vec(5); // 创建一个大小为5的向量  
    for (int i = 0; i < vec.size(); i++) 
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    for (int i = 0; i < vec.size(); i++)
    {
        vec[i] = 2*i;
        cout << vec[i] << " ";
    }
    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    vec.reserve(3); // 改变向量的大小为3
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    vec.reserve(9);
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
    cout << "===========================================================" << endl;

    return 0;
}

输出:

0 0 0 0 0
0 2 4 6 8
vec.size():5, vec.capacity():5
===========================================================
0 2 4 6 8
vec.size():5, vec.capacity():5
===========================================================
0 2 4 6 8
vec.size():5, vec.capacity():9
===========================================================

例1:

#include<iostream>  
#include<vector>  
using namespace std;

void printVector(vector<int>& v)
{	//利用迭代器打印 v
    int i = 0;
    for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
    {
        cout  << *it << " ";
        i++;
        if(i%10 == 0)
            cout << endl;
    }
    cout << endl;
}

int main(int argc, char* argv[])
{
    vector<int> myVec;
    myVec.reserve(100);     // 新元素还没有构造,
    // 此时不能用[]访问元素
    for (int i = 0; i < 100; i++)
    {
        myVec.push_back(i); //新元素这时才构造
    }
    myVec.resize(102);      // 用元素的默认构造函数构造了两个新的元素
    myVec[100] = 1;           //直接操作新元素
    myVec[101] = 2;

    printVector(myVec);
    return 0;
}

5.8综合示例

// vectorsample.cpp : 定义控制台应用程序的入口点。  
//  
//#include "stdafx.h"
#include<iostream>  
#include<vector>  
#include<string>  
using namespace std;

class Student
{
public:
    string m_strNO;
    string m_strName;
    string m_strSex;
    string m_strDate;
public:
    Student(string strNO, string strName, string strSex, string strDate)
    {
        m_strNO = strNO;
        m_strName = strName;
        m_strSex = strSex;
        m_strDate = strDate;
    }
    void Display()
    {
        cout << m_strNO << "\t";
        cout << m_strName << "\t";
        cout << m_strSex << "\t";
        cout << m_strDate << "\t";
    }
};

class StudCollect
{
    vector<Student> m_vStud;
public:
    void Add(Student& s)
    {
        m_vStud.push_back(s);
    }
    Student* Find(string strNO)
    {
        bool bFind = false;
        int i;
        for (i = 0; i < m_vStud.size(); i++)
        {
            Student& s = m_vStud.at(i);
            if (s.m_strNO == strNO)
            {
                bFind = true;
                break;
            }
        }
        Student* s = NULL;
        if (bFind)
            s = &m_vStud.at(i);
        return s;
    }
};

int main(int argc, char* argv[])
{
    Student s1("1001", "zhangsan", "boy", "1988-10-10");
    Student s2("1002", "lisi", "boy", "1988-8-25");
    Student s3("1003", "wangwu", "boy", "1989-2-14");

    StudCollect s;
    s.Add(s1);
    s.Add(s2);
    s.Add(s3);

    Student* ps = s.Find("1002");
    if (ps)
        ps->Display();
    return 0;
}

代码运行实例如下:

1002    lisi    boy     1988-8-25

参考:vector容器用法详解 - 蒲公英110 - 博客园 (cnblogs.com)

6.vector容器的底层实现机制

作者建议还是稍微看一下底层机制,vector主要有三个指针(迭代器) 来表示的:

●_Myfirst 和 _Mylast 可以用来表示 vector 容器中目前已被使用的内存空间;
●_Mylast 和 _Myend 可以用来表示 vector 容器目前空闲的内存空间;
●_Myfirst 和 _Myend 可以用表示 vector 容器的容量。

对于空的 vector 容器,由于没有任何元素的空间分配,因此 _Myfirst、_Mylast 和 _Myend 均为 null。

vector扩容本质

另外需要指明的是,当 vector 的大小和容量相等(size==capacity)也就是满载时,如果再向其添加元素,那么 vector 就需要扩容。vector 容器扩容的过程需要经历以下 3 步:

1.完全弃用现有的内存空间,重新申请更大的内存空间;
2.将旧内存空间中的数据,按原有顺序移动到新的内存空间中;
3.最后将旧的内存空间释放。

这也就解释了,为什么 vector 容器在进行扩容后,与其相关的指针、引用以及迭代器可能会失效的原因。

由此可见,vector 扩容是非常耗时的。为了降低再次分配内存空间时的成本,每次扩容时 vector 都会申请比用户需求量更多的内存空间(这也就是 vector 容量的由来,即 capacity>=size),以便后期使用。

vector 容器扩容时,不同的编译器申请更多内存空间的量是不同的。以 VS 为例,它会扩容现有容器容量的 50%。

参考:C++ STL vector容器(深入了解,一文学会)

posted @ 2023-03-23 21:53  CodeMagicianT  阅读(43)  评论(0编辑  收藏  举报