【C++】vector的介绍 | 常见接口的使用

目录

vector的介绍

常见接口

构造函数

尾插push_back()

vector的遍历

1.用方括号+下标 遍历:

2.调用at()来访问:

3.用迭代器遍历:

4.范围for遍历:

vector的空间修改

vector增删查改

覆盖assign()

查找find()

插入insert()

删除erase()

排序sort()

string与vector的区别


本篇先对vector做一个介绍,然后把常用的接口演示一下。

由于vector的许多接口和string那儿的高度重合,所以这里就不详讲了,主要做一个了解。只要掌握几个最重要的接口,其余的用到时 查文档就好。

vector的介绍

vector是一个类模板。它能够容纳各种类型的对象作为其元素,并且可以动态地调整大小。可以理解为动态数组

它像数组,又不像。

像数组的是:vector也采用的连续存储空间来存储元素。这意味着可以采用下标对vector的元素进行访问,和数组一样高效。

不像数组的是:它的大小是可以动态改变的,而且它的大小会被容器自动处理。

本质来说,vector使用动态分配数组来存储它的元素。

当新元素插入时候,这个数组需要被重新分配大小,为了增加存储空间。

其做法是,分配一个新的数组,然后将全部元素移到这个数组。

就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。

vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。

不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。

因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。

与其它动态序列容器相比(deque、list 、forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。

对于其他不在末尾的删除和插入操作,效率更低。

常见接口

包头文件:

#include

构造函数

两个标了的要记住:

构造函数声明 接口说明
vector();(重点) 无参构造
vector(size_type n, const value_type& val = value_type()); 构造并初始化n个val
vector (const vector& x); 拷贝构造
vector (InputIterator first, InputIterator last); 使用迭代器进行初始化构造

我们在使用时,要注意vector是一个类模板,要实例化成 具体的类型 来使用:

vector v1;        //创建一个int类型的,空的v1
​
vector v2(5, 1);  //v2含5个1
​
vector v3(v2);   //拷贝构造
​
vector v4(v3.begin(), v3.end());   //用迭代器

尾插push_back()

vector没有头插 / 头删(因为每次都要挪数据,效率太低),也不能用operator+=,

它只能尾插 / 尾删,或者insert/erase。总之,就是一个一个地插入、删除。

#include
using namespace std;
​
int main()
{
    vector v1;      
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    return 0;
}

相关接口:尾删pop_back()

vector的遍历

先说明一下:

vector是不能直接用流插入输出的:

【C++】vector的介绍 | 常见接口的使用_第1张图片

因为vector是一个容器,它可以存储多个元素。

而流插入输出操作符<<是用于将一个单独的元素插入到流中的,而不是将整个容器插入到流中。

因此,如果要将vector中的元素输出到流中,需要使用循环逐个输出

现在我们就来看看遍历输出vector的4种方法:

1.用方括号+下标 遍历:

vector是支持operator[]和size()的,因此可以用 方括号+下标 的方式遍历。

#include
#include
using namespace std;
​
int main()
{
    vector v;      
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
​
    for (size_t i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }
    return 0;
}

2.调用at()来访问:

【C++】vector的介绍 | 常见接口的使用_第2张图片

at()是像函数一样调用的:

for (size_t i = 0; i < v.size(); i++)
    {
        cout << v.at(i) << " ";
    }

这里补充一下at()和operator[]的区别:两者检查越界的方式不一样。

at()是较温和的抛异常:

【C++】vector的介绍 | 常见接口的使用_第3张图片

而operator[]是断言:

【C++】vector的介绍 | 常见接口的使用_第4张图片

3.用迭代器遍历:

vector::iterator it = v.begin();
while (it != v.end())
{
    cout << *it << " ";
    it++;
}

4.范围for遍历:

for (auto& e : v)
{
    cout << e << " ";
}

vector的空间修改

容量空间 接口说明
size 获取数据个数
capacity 获取容量大小
empty 判断是否为空
resize (重点) 改变vector的size
reserve (重点) 改变vector的capacity

这些接口的功能、用法都和string里的类似,就不详讲了。

这里要说下,capacity在vs下和在g++下的增长问题:

vs下capacity是按1.5倍增长的,g++是按2倍增长的。

这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。

来测试下:

vs

#include
#include
using namespace std;
​
int main()
{
    vector v;   
    int val = v.capacity();
    for (size_t i = 0; i < 100; i++)
    {
        v.push_back(i);
        if (val != v.capacity())
        {
            cout <<"now the capacity is:  "<< v.capacity() << endl;
        }
        val = v.capacity();
    }
    return 0;
}

【C++】vector的介绍 | 常见接口的使用_第5张图片

g++:

【C++】vector的介绍 | 常见接口的使用_第6张图片

vector增删查改

覆盖assign()

【C++】vector的介绍 | 常见接口的使用_第7张图片

assign:将新内容指定给vector,替换其当前内容,并相应地修改其size。也就是说,起到一个替换、覆盖的作用。

演示:

#include
#include
using namespace std;
​
int main()
{
    vector v;   
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    for (auto& e : v)
    {
        cout << e << " ";
    }
    cout << endl;
​
    v.assign(8, 8);
    for (auto& e : v)
    {
        cout << e << " ";
    }
    return 0;
}

查找find()

其实vector的接口里并未提供find(),不光是vector,list、deque等容器中也没有该函数。

这是因为像vector、list、deque等容器,它们都是从头到尾遍历查找,查找逻辑是相同的。

而在算法库中,find()是一个类模板算法,所有的容器都可以复用,想用时直接套上去用,因此没必要单独实现find()接口了。

注意:

1.要包头文件。

2.迭代器区间,都是左闭右开的 :[ first,last )

3.返回的是迭代器。如果找到了val,返回val的迭代器;如果没找到,返回last,即右边开区间的位置。

示例:

#include
#include
#include
using namespace std;
​
int main()
{
    vector v;   
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    vector::iterator it = find(v.begin(), v.end(), 3);
    cout << *it << endl;
    return 0;
}

插入insert()

有了insert(),我们可以实现自由插入了,包括头插。

现在我们就在2的前面插入一个100:

int main()
{
    vector v;   
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    
    vector::iterator pos = find(v.begin(), v.end(), 2);   //先找到2的位置pos
    if (pos != v.end())
    {
        vector::iterator it = v.insert(pos, 100);     //在pos位置插入100,2被挤到后一个
    }
    
    for (auto& e : v)
    {
        cout << e << " ";
    }
    return 0;
}

删除erase()

【C++】vector的介绍 | 常见接口的使用_第8张图片

示例:

int main()
{
    vector v;   
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
​
    vector::iterator pos = find(v.begin(), v.end(), 2);
    if (pos != v.end())
    {
        v.erase(pos);
    }
    for (auto& e : v)
    {
        cout << e << " ";
    }
    return 0;
}

排序sort()

sort()和find()一样,都是在中的类模板算法,可以被不同的容器复用。

【C++】vector的介绍 | 常见接口的使用_第9张图片

➡️排序算法默认排的是升序,例:

#include
#include
#include
using namespace std;
int main()
{
    vector v;   
    v.push_back(11);
    v.push_back(8);
    v.push_back(3);
    v.push_back(4);
    v.push_back(0);
    v.push_back(4);
    v.push_back(9);
    v.push_back(7);
​
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
​
    sort(v.begin(), v.end());
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
    return 0;
}

➡️那要怎么排降序呢?

用仿函数(仿函数目前不详讲,现在会用就行),库里面已经写好了,直接拿来用即可。

template   
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);   //仿函数Compare comp

两个仿函数,一个叫less(<,升序),一个叫greater(>,降序),两个都是类模板。

注意:在使用时要包头文件。

【C++】vector的介绍 | 常见接口的使用_第10张图片

示例:

#include
#include
#include
#include
using namespace std;
​
int main()
{
    vector v;   
    v.push_back(11);
    v.push_back(8);
    v.push_back(3);
    v.push_back(4);
    v.push_back(0);
    v.push_back(4);
    v.push_back(9);
    v.push_back(7);
​
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
​
    less lt;
    greater gt;
    sort(v.begin(), v.end(), lt);   //升序
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
​
    sort(v.begin(), v.end(), gt);   //降序
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
​
    return 0;
}

【C++】vector的介绍 | 常见接口的使用_第11张图片

不过,更常见的是用匿名对象来写:

sort(v.begin(), v.end(), greater()); 

string与vector的区别

既然vector是类模板,那vector能代替string吗?

答案当然是不能。

原因:

1.string结尾是有'\0'的,而vector没有。

2.很多string的接口,vector都用不起来,如+=、find、<<、>>、比较大小、to_string等。

所以,几乎不会用vector,一般直接用string。

你可能感兴趣的:(c++,开发语言)