写这篇博客的缘由是我开始通过LeetCode刷题来训练我学习C++的计划时,最开始选择的题目类别是数组题。那么自然而言要涉及到C++中STL(标准库)的Vector容器了。其实在做题过程中我还涉及到诸如Set、Map等的容器,那么我会在后续的学习中,通过博文的形式记录一下学习历程。
这将是我首次系统的重新学习Vector容器。之前有通过刷华为OJ的题积累过Vector的相关用法,但是并不系统全面,因此,这次我将重新学习该容器,并通过博客的形式系统的学习Vector。
学习的主要参考资料有:
当然这些都是我目前给予自己理解的浅薄的学习总结。日后若需要继续深入学习还可以不断雕磨细节,不断完善自己的知识体系。博观约取,厚积薄发。
STL vector 类是序列容器的一个模板类,和数列类似,vector将元素储存在邻近的存储位置中,因此可以通过相对元素位置的指针偏移来访问各个元素。vector容器将给定类型的元素以线性排列方式进行排列,并且允许快速随机访问任何元素。不同于数列,vector能够动态改变其大小,而且能够储存不同类型的元素。因此,vector应是随机访问性能最佳时的首选序列容器。使用时需要包含头文件:
#include
vector语法表达如下:
template < class T, class Alloc = allocator > class vector; // generic template
这里面有两个参数:
第一个参数好理解,但是第二个参数涉及到Allocator,分配器的知识我不太了解。通过网上知识查询大概了解到这是一种内存分配方法。一般使用各种容器都是使用默认类的。这里我就挖个坑,等日后通过不断练习对这个概念有了一定经验上的认识后再来仔细深入研究。这里贴一些网络资料。
1.创建一个空vector,无元素填充,无固定长度
vector v0; // Create an empty vector v0
2.创建设定元素个数的vector,元素值没确定,一般会自动设定为默认值0
vector v1(3); // Create a vector v1 with 3 elements of default value 0
3.创建设定元素个数,并且设定元素数值,但默认所有元素数值都一样
vector v2(5, 2); // Create a vector v2 with 5 elements of value 2
4.按照之前创建的v2的分配器,重新创建一个vector,三个元素,值都为1。其实这里只是举个例子,其实也可以用别的分配器。
// Create a vector v3 with 3 elements of value 1 and with the allocator of vector v2
vector v3(3, 1, v2.get_allocator());
5.通过拷贝的方式,创建一个vector
vector v4(v2); // Create a copy, vector v4, of vector v2
6.首先创建一个空vector,然后往里面逐个填充元素
// Create a new temporary vector for demonstrating copying ranges
vector v5(5);
for (auto i : v5) {v5[i] = i;}
7.通过指定复制元素范围来创建vector
// Create a vector v6 by copying the range v5[ first, last)
vector v6(v5.begin() + 1, v5.begin() + 3);
8.通过数组创建vector
vector v8{ { 1, 2, 3, 4 } };
//或者事先已经存在数组,将数组中特定元素传入vector
int n[] = {1, 2, 3, 4, 5} ;
vector a(n, n+5) ; //将数组n的前5个元素作为向量a的初值
vector a(&n[1], &n[4]) ; //将n[1] ~ n[4]范围内的元素作为向量a的初值
9.未知大小的二维向量创建
vector > ivec;
//注意:vector"之间要有空格!否则会被认为是重载">>"
10.创建row*col的二维向量创建
//其实我们可以重新理解一些二维vector的创建过程
//首先创建一个长度为row的一维vector:vector ivec(row,a);
//这个一维vector长度为row,里面的元素是type类的a,其实a也是一个长度为col的vector:vector a(col)
//这个a的类型也为vector,所以整合一下就有
vector >ivec(row,vector(col));
11.更进一步创建所有元素均为a的row*col的二维向量
vector > ivec(row,vector(col,a));
详细的vector操作符运算说明文档可以参考:vector运算符。
1.对于operator !=与operator==,两边运算对象均为vector,不仅要考虑vector元素的类型,还要有个数,与类型对应的数值。当两个vector完完全全一样,==会返回true;有一点不一样,!=会返回true。
2.对于大小比较运算符,两边运算对象均为vector。那么将会从第一个元素开始比较,然后比较到第一个能得出运算结果的元素,返回该结果作为运算符比较结果,比如:
#include
#include
int main( )
{
using namespace std;
vector v1, v2;
v1.push_back( 1 );
v1.push_back( 3 );
v1.push_back( 1 );
v2.push_back( 1 );
v2.push_back( 2 );
v2.push_back( 2 );
if ( v1 > v2 )
cout << "Vector v1 is greater than vector v2." << endl;
else
cout << "Vector v1 is not greater than vector v2." << endl;
//Output: Vector v1 is greater than vector v2.
}
vector中主要迭代器如下表所示:
Iterators | Return Value |
---|---|
begin | Return iterator to beginning |
end | Return iterator to end |
rbegin | Return reverse iterator to reverse beginning |
rend | Return reverse iterator to reverse end |
cbegin | Return const_iterator to beginning |
cend | Return const_iterator to end |
crbegin | Return const_reverse_iterator to reverse beginning |
crend | Return const_reverse_iterator to reverse end |
这里有c为前缀的,是C++11新标准添加的迭代器,c代表const,就是不能修改的。下面可以通过MSDN上的Example(以begin与cbegin为例)来加强理解,此外,在程序的最后还添加了rbegin相关代码。
#include
#include
int main()
{
using namespace std;
vector c1; //创建一个新vector
vector::iterator c1_Iter; //创建一个迭代器
vector::const_iterator c1_cIter; //创建一个常量迭代器
c1.push_back(1);
c1.push_back(2); //向c1填入元素1、2
cout << "The vector c1 contains elements:";
c1_Iter = c1.begin();
for (; c1_Iter != c1.end(); c1_Iter++)
{
cout << " " << *c1_Iter;
//Output:The vector c1 contains elements: 1 2
}
cout << endl;
cout << "The vector c1 now contains elements:";
c1_Iter = c1.begin();
*c1_Iter = 20;
for (; c1_Iter != c1.end(); c1_Iter++)
{
cout << " " << *c1_Iter;
//Output:The vector c1 contains elements: 20 2
}
cout << endl;
*c1_cIter = 200; // This line would be an error because iterator is const
//rbegin相关代码
v1_rIter = v1.rbegin( );
cout << "The first element of the reversed vector is "
<< *v1_rIter << "." << endl;
}
从代码中可以得知,begin就是返回vector的第一个元素的随机访问迭代器,可以分配给vector::interator,也可以分配给vector::const_interator。但前者可以修改对象,后者不可以。rbegin则主要是反向返回从后往前发现的第一个元素(也就是从前往后的最后一个元素)的迭代器。同begin一样,可以分配给vector::interator与vector::const_interator。但是,修改时,rbegin++其实就是从后往前遍历,与begin++正好相反。
与vector的容量有关的函数整理如下:
Capacity | Return Value or Function |
---|---|
size | Return size |
max_size | Return maximum size |
resize | Change size |
capacity | Return size of allocated storage capacity |
empty | Test whether vector is empty |
cend | Return const_iterator to end |
reserve | Request a change in capacity |
shrink_to_fit | Shrink to fit |
vector的跟容器容量有关的函数整理如上,接下来对常用的进行细致的讨论:
1.vector::size.返回向量中的元素数量,也可以理解为返回向量当前的长度。
2.vector::max_size.返回向量的最大长度,也可以理解为最大可取长度。
3.vector::capacity.返回在不分配更多的存储的情况下向量可以包含的元素数,也可以理解为该向量的当前储存长度。
关于上面三个函数,可以参考下面这段代码帮助理解:
#include
#include
int main ()
{
std::vector myvector;
// set some content in the vector:
for (int i=0; i<100; i++) myvector.push_back(i);
std::cout << "size: " << myvector.size() << "\n";
std::cout << "capacity: " << myvector.capacity() << "\n";
std::cout << "max_size: " << myvector.max_size() << "\n";
return 0;
//Output Could be:
//size: 100
//capacity: 128
//max_size: 1073741823
}
4.vector::resize.为向量指定新的大小。有两种调用方式:
void resize(size_type Newsize);
void resize(size_type Newsize, Type Val);
**注意:**参数说明:Newsize:矢量的新大小;Val:新大小大于旧大小时添加至矢量的新元素的初始化值。如果省略该值,则新对象将使用默认构造函数。
**注意:**如果容器的大小小于请求的大小 Newsize,那么会在矢量中添加元素,直到该容器达到请求的大小。 如果容器的大小大于请求的大小,最接近容器末尾的元素将被删除,直到该容器达到大小 Newsize。 如果容器的当前大小与请求的大小相同,则不采取任何操作。
#include
#include
int main( )
{
using namespace std;
vector v1;
vector ::size_type i;
v1.push_back( 1 );
i = v1.size( );
cout << "Vector length is " << i << "." << endl;
v1.push_back( 2 );
i = v1.size( );
cout << "Vector length is now " << i << "." << endl;
//Output:
//Vector length is 1.
//Vector length is now 2.
}
5.vector::reserve.为向量对象保留最小的存储长度。调用方式为:
void reserve(size_type count);
注意: count是要分配给向量的最小存储长度。
可以参考这段代码帮助理解:
#include
#include
int main( )
{
using namespace std;
vector v1;
//vector ::iterator Iter;
v1.push_back( 1 );
cout << "Current capacity of v1 = "
<< v1.capacity( ) << endl;
v1.reserve( 20 );
cout << "Current capacity of v1 = "
<< v1.capacity( ) << endl;
//Output:
//Vector length is 1.
//Vector length is now 2.
}
关于resize与reserve的区别可以参考这篇博客:C++:vector中的resize()函数 VS reserve()函数与c++ vector resize & reserve。问题的关键在于基本概念:
那么进一步就有:
6.vector::empty.该函数测试是否该向量为空。返回bool类型,true表示向量为空,false表示向量不为空。
Element access: | Function |
---|---|
operator[] | Access element |
at | Access element |
front | Access first element |
back | Access last element |
data | Access data |
reference at(size_type _Pos);
const_reference at(size_type _Pos) const;
参数_Pos:要在矢量中引用的元素的下标或位置编号。
返回值:对自变量中的下标元素的引用。 如果_Off大于该向量中的大小在将引发异常。如果返回值为在分配给const_reference,无法修改矢量对象。 如果返回值为在分配给引用,可以修改矢量对象。
3.vector::front.返回值:对向量对象中第一个元素的引用。 如果向量为空,则返回值不确定。
4.vector::bakc.返回值:对向量对象中最后一个元素的引用。 如果向量为空,则返回值不确定。
Modifiers: | Function |
---|---|
assign | Assign vector conten |
push_back | Add element at the end |
pop_back | Delete last element |
insert | Insert elements |
erase | Erase elements |
swap | Swap content |
clear | Clear content |
emplace | Construct and insert element |
emplace——back | Construct and insert element at the end |
1.vector::push_back.在矢量末尾处添加一个元素。
2.vector::pop_back.删除矢量末尾处的元素。
3.vector::insert.将一个、多个或一系列元素插入到指定位置的向量中。insert有多种调用方法,具体可以参考代码:
#include
#include
int main( )
{
using namespace std;
vector v1;
vector ::iterator Iter;
v1.push_back( 10 );
v1.push_back( 20 );
v1.push_back( 30 );
cout << "v1 =" ;
for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ )
cout << " " << *Iter;
cout << endl;
v1.insert( v1.begin( ) + 1, 40 );
cout << "v1 =";
for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ )
cout << " " << *Iter;
cout << endl;
v1.insert( v1.begin( ) + 2, 4, 50 );
cout << "v1 =";
for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ )
cout << " " << *Iter;
cout << endl;
v1.insert( v1.begin( )+1, v1.begin( )+2, v1.begin( )+4 );
cout << "v1 =";
for (Iter = v1.begin( ); Iter != v1.end( ); Iter++ )
cout << " " << *Iter;
cout << endl;
// initialize a vector of vectors by moving v1
vector < vector > vv1;
vv1.insert( vv1.begin(), move( v1 ) );
if ( vv1.size( ) != 0 && vv1[0].size( ) != 0 )
{
vector < vector >::iterator Iter;
cout << "vv1[0] =";
for (Iter = vv1[0].begin( ); Iter != vv1[0].end( ); Iter++ )
cout << " " << *Iter;
cout << endl;
}
//Output
//v1 = 10 20 30
//v1 = 10 40 20 30
//v1 = 10 40 50 50 50 50 20 30
//v1 = 10 50 50 40 50 50 50 50 20 30
//vv1[0] = 10 50 50 40 50 50 50 50 20 30
}
4.vector::erase.从指定位置删除向量中的一个元素或一系列元素.调用方法有:
iterator erase(
const_iterator _Where);
iterator erase(
const_iterator first,
const_iterator last);
代码示例可以参考如下:
#include
#include
int main( )
{
using namespace std;
vector v1;
vector ::iterator Iter;
v1.push_back( 10 );
v1.push_back( 20 );
v1.push_back( 30 );
v1.push_back( 40 );
v1.push_back( 50 );
cout << "v1 =" ;
for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ )
cout << " " << *Iter;
cout << endl;
v1.erase( v1.begin( ) );
cout << "v1 =";
for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ )
cout << " " << *Iter;
cout << endl;
v1.erase( v1.begin( ) + 1, v1.begin( ) + 3 );
cout << "v1 =";
for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ )
cout << " " << *Iter;
cout << endl;
//Output
//v1 = 10 20 30 40 50
//v1 = 20 30 40 50
//v1 = 20 50
}
5.vector::swap.交换两个向量的元素。调用方法如下:
void swap(
vector& right);
friend void swap(
vector& left,
vector& right);
参数:
right:提供要交换的元素的向量,或其元素将要与向量 left 的元素进行交换的向量。 left:一个向量,其元素将与向量 right 的元素进行交换。
6.vector::clear.清除向量的元素。调用:v1.clear().v1就空了。
这是目前我对vector的使用总结。更重要的是要掌握灵活使用vector的方法、场景。此外,还需要与其他容器进行性能上的比较。这样才能用的好、用的巧。那么,以后碰到vector的新的知识点我会及时更新。接下来会进行另一个容器map的学习。