C++《vector》

mljy. 2024-10-24 17:35:02 阅读 89

#1024程序员节|征文#

在之前C++《string》当中我们学习了string的各个接口的使用以及在string模拟实现当中试着实现了string当中日常我们会较为频繁使用到的接口,通过模拟实现我们对string的底层有了更深层次的理解。接下来在本篇当中我们将进行进行STL的学习,在本篇我们要学习的容器是vector也就是之前在数据结构学习过的顺序表,在学习vector过程中由于STL的接口大部分都是相识的因此在学习完string后学习vector将会轻松许多。在了解vector过程中最重要的是要了解深浅拷贝,这部分的内容将在本篇之后的vector模拟实现当中重点来讲解。接下来就开始本篇的学习吧!!!


1.⭐构造函数

vector::vector - C++ Reference

vector当中构造函数提供了以上的4个接口,这其中包括一个拷贝构造函数,接下来我们就来一一了解这些接口该如何使用

在了解vector的构造之前先要了解的是STL当中的vector是用模板实现的,这样是为了vector内的数据类型不被限定死,这样创建的vextor对象内的数据类型就可以根据用户显示写大的来确定

vector构造函数的接口当中以下几个是和我们之前学习的string类似的

<code>#include<iostream>

#include<vector>

using namespace std;

int main()

{

//无参构造

vector<int> v1;

//一个元素构造

vector<int> v2(1);

//n个一样的元素构造

vector<int> v3(5, 6);

return 0;

}

接下来要了解的两个接口就不和之前学习的string不同

template <class InputIterator>

vector (InputIterator first, InputIterator last,const allocator_type& alloc =allocator_type());

以上这个接口是使用迭代器区间来实现vector对象的初始化构造,在此迭代器fist是输入的开始,last迭代器是结束位置,最终范围是 [first,last),该区间是左闭右开的,构造的过程中不包括last指向的值

使用例如以下示例

#include<iostream>

#include<vector>

using namespace std;

int main()

{

//无参构造

vector<int> v1;

//一个元素构造

vector<int> v2(1);

//n个一样的元素构造

vector<int> v3(5, 6);

//使用迭代器区间来构造

vector<int> v4(v3.begin(), v3.end()-1);

return 0;

}

在以上代码中对象v4的初始化就是使用迭代器区间来实现,在此开始时从v3.begin(),结束位置是v3.end(),并且最后的数据不包括v3.end()位置的元素 

因此v4初始化之后该对象内部数据就为6 6 6 6 

在此我们还要了解vector初始化的方式是通过调用initializer_list来完成初始化,这个接口是在C++11之后支持的

要了解这个构造的接口是如何实现的我们就要先来了解initializer_list是什么

initializer_list是一个C++11当中增加的,在此我们可以认为将一系列的数据使用{}括起来就会初始化出一个对象,该对象的类型就是initializer_list。可以认为initializer_list也是一个容器

 

在initializer_list当中成员函数非常的简单,就支持迭代器和size

在了解了initializer_list之后我们就可以知道在vector构造当中提供的initializer_list接口就可以实现用户直接将使用{}括起来的数据直接给vector对象来实现初始化构造,在该过程其实可以理解为会在底层开出一块内存空间将{}内的数据存储到内存空间内,并且还会有两个指针分别指向空间的起始和终止位置,最后将该空间内的数据依次插入到vector对象内实现初始化

例如以下示例:

<code>#include<iostream>

#include<vector>

using namespace std;

int main()

{

vector<int> v5({ 1,2,3,4 });

vector<int> v6 = { 1,2,3,4 };

return 0;

}

在以上代码中对象v5就是通过调用vector构造函数当中的initializer_list接口来直接完成构造。而对象v6其实语法逻辑和v5有所不同的是通过隐式类型转换,是将initializer_list对象先构造出一个vector的临时对象之后再去拷贝构造,但编译器在这样的连续构造+拷贝构造就会演化为直接构造

接下来了解vector当中的拷贝构造函数

vector(const vector& x)就是将对象x的拷贝数据拷贝给新创建的对象,在此过程进行的是深拷贝,具体实现在之后的vector模拟实现章节会进行解析

例如以下示例:

#include<iostream>

#include<vector>

using namespace std;

int main()

{

vector<int> v6 = { 1,2,3,4 };

vector<int> v7(v6);

return 0;

}

以上代码就是使用对象v6来拷贝构造v7

2.⭐析构函数

vector::~vector - C++ Reference

 在vector当中也提供了析构函数,该函数会在对象销毁时自动调用,不需要我们显示的调用

3.⭐赋值运算符重载

vector::operator= - C++ Reference

在vector当中也提供了赋值运算符重载函数,有了这个函数我们就可以将两个已经初始化的vector对象之间进行赋值

例如以下示例:

<code>#include<iostream>

#include<vector>

using namespace std;

int main()

{

//无参构造

vector<int> v1;

//一个元素构造

vector<int> v2(1);

v1 = v2;

return 0;

}

以上就在对象v1和v2在初始化之后将对象v2赋值给v1

4.容量操作

在vector当中容量的操作除了以下讲解的其实还有其他的操作,但其他的函数操作在日常当中的使用较少,并且这些函数和之前我们学习string时类似,因此你如果想详细了解可以观看以下文档

vector - C++ Reference

4.1 ⭐size

vector类当中也提供了size这个函数来得到对象内的有效元素个数

4.2 ⭐capacity

vector类中的capacity函数是用来得到类对象当中的空间大小也就是容量 

4.3 ⭐resize

vector类当中也提供了resize来将对象内的有效元素个数改为指定值n,这时就会出现以下三种情况

1.此时的有效数据个数比n大,那么调用resize之后就会将原有效数据个数截断到n个

2. 此时的有效数据个数比n小,并且n比当前的内存空间大小小,那么这时调用resize就会将对象当中从原有效数据之后到n个元素都使用指定值val来填充

3..此时的当前的内存空间大小比n小,那么这时调用resize就会先将该对象进行内存空间的扩容之后再进行以上的2操作

4.4⭐ reserve

vector类当中也提供了reserve来实现将对象的内存空间扩容到指定值n,当n大于原空间大小时会进行扩容,但当n小于原空间的大小时一般不会进行缩容

5. 访问以及遍历操作

5.1 ⭐下标+[ ]

vector::operator[] - C++ Reference

vector当中由于底层和顺序表类似物理空间是连续的因此能通过下标+[ ]的方式来访问vector对象内的元素,并且和string一样也提供了const对象和非const对象两个接口。对于非const对象来说使用下标+[ ]访问元素既可读也可以写;但对于const对象来说使用下标+[ ]访问元素只能读

例如以下示例:

<code>#include<iostream>

#include<vector>

using namespace std;

int main()

{

vector<int> v3(5, 6);

for (int i = 0; i < v3.size(); i++)

{

cout << v3[i] << " ";

}

return 0;

}

5.2 ⭐迭代器

vector类当中和string一样也提供了正向迭代器和 反向迭代器,并且以上迭代器都提供了const接口和非const接口

例如以下示例:

<code>#include<iostream>

#include<vector>

using namespace std;

int main()

{

vector<int> v5({ 1,2,3,4 });

//vector<int>::iterator i1 = v5.begin();

auto i1 = v5.begin();

while (i1 != v5.end())

{

cout << *i1 << " ";

i1++;

}

cout << endl;

auto i2 = v5.rbegin();

while (i2 != v5.rend())

{

cout << *i2 << " ";

i2++;

}

cout << endl;

return 0;

}

5.3 ⭐范围for

在vector当中由于支持迭代器那么就可以支持范围for来遍历对象内的元素

例如以下示例:

#include<iostream>

#include<vector>

using namespace std;

int main()

{

vector<int> v6 = { 1,2,3,4 };

for (auto x : v6)

{

cout << x << " ";

}

return 0;

}

6.修改操作

在vector提供了以下的函数来实现对对象内数据的修改

以上两个函数emplace和emplace_back是string中没有的,在此在本篇当中先不讲这两个函数该如何使用,这两个函数list当中也是有的因此将在list篇章中细致的讲解

6.1 assign

vector::assign - C++ Reference

vector当中也支持assign将原vector对象内的数据进行重覆盖,在此和string不同的是在调用这个接口需要传的是迭代器区间

例如以下示例:

<code>#include<iostream>

#include<vector>

using namespace std;

int main()

{

vector<int> v5({ 7,8,9,10 });

vector<int> v6 = { 1,2,3,4 };

for (auto x : v6)

{

cout << x << " ";

}

return 0;

}

6.2 ⭐push_back与pop_back

vector当中提供 push_back与pop_back两个函数分别来实现尾插和尾删

例如以下示例:

<code>#include<iostream>

#include<vector>

using namespace std;

int main()

{

vector<int> v6 = { 1,2,3,4 };

v6.pop_back();

v6.push_back(88);

v6.push_back(99);

for (auto x : v6)

{

cout << x << " ";

}

return 0;

}

6.3 insert和erase

vector::insert - C++ Reference

vector::erase - C++ Reference

vector当中也提供了insert和erase来实现任意位置的插入和删除

在此的插入和删除要传的参数和之前string有所不同,vector中的inser和erase需要传的是迭代器区间或者是一个迭代器

例如以下示例:

<code>#include<iostream>

#include<vector>

using namespace std;

int main()

{

vector<int> v5({ 7,8,9,10 });

vector<int> v6 = { 1,2,3,4 };

v6.insert(v6.begin(), 999);

v6.insert(v6.begin()+1,2, 888);

v6.erase(v6.end() - 1);

v6.erase(v6.end() -3, v6.end() - 1);

for (auto x : v6)

{

cout << x << " ";

}

return 0;

}

注意:在使用迭代器区间删除时,删除区间是左闭右开的,删除不包括最后位置的值

 

 

以上就是vector使用的介绍,通过本篇讲解你会发现string和vector内大部分的函数都是相识的,因此在学习了string之后再了解vector就任意多了,这是因为STL大部分都是相通的,学习了一个容器之后后面的容器的使用就较为任意上手了。

本篇的内容到这里就结束了,接下来再下一篇vector模拟实现当中我们将会来试着自己实vector,在这个过程当中会了解到vector迭代器失效这个在vector非常重要的知识点,未完待续……

 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。