文章目录
由数组到vector
要素 | 注释 |
---|---|
与数组一样的地方 | vector是采用连续存储空间来存储元素的序列式容器。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。 |
与数组不一样的地方 | vector的大小是可以动态改变的。vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。 |
vector分配空间调整 | 不同的库采用不同的策略权衡空间的使用和重新分配。以达到在末尾插入一个元素的时候是在常数时间的复杂度完成的。 |
vector性能 | vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。 |
vector跟其它动态序列容器相比 | vector在访问元素的时候更加高效(可以通过下标访问),在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。 |
vector的使用
vector构造函数
构造函数声明 | 释义 |
---|---|
vector() | 无参构造 |
vector(size_type n,const value_type& value=value_type()) | 构造并初始化前n个元素,第二个参数缺省为0 |
vector (InputIterator first, InputIterator last) | 迭代器构造 |
vector(const vector& x) | 用vector去拷贝构造 |
使用数组构造vector更加方便
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int len = sizeof(arr) / sizeof(arr[0]);
vector<int> vec(arr, arr + len);
for (vector<int>::iterator it = vec.begin(); it != vec.end(); it++)
{
cout << *it << endl;
}
system("pause");
return 0;
}
vector iterator 的使用
接口 | 说明 |
---|---|
begin() | 第一个元素的iterator |
end() | 最后一个元素的下一个位置的iterator |
rbegin() | 最后一个元素的iterator |
rend() | 第一个元素的前一个位置的iterator |
cbegin() | 第一个元素的const_iterator |
cend() | 获得最后一个元素的下一个位置的const_iterator |
vector的空间增长问题
接口 | 释义 |
---|---|
size() | 或者vector的元素个数 |
max_size() | 获取vector的 |
capacity() | 获得vector的容量 |
empty() | 判断vector是否为空 |
void resize (size_type n, value_type val = value_type()); | 改变vector的size |
void reserve (size_type n); | 改变vector的capacity |
resize()和reserve()函数
resize(size_type n,value_type val=value_type());
resize()有点vector重构造的感觉,第二个参数只是“备胎”,这个参数只是给新增有效空间的默认值。如果不给第二个参数,缺省值是0。第一个参数是改造之后的vector的大小,如果第一个参数比原来的size()要小,会丢失数据。
reserve(size_type n);
reserve()函数只是改变容量,参数n是修改之后容量的大小。
reserve的参数小于vector的size(),reserve函数不会造成数据丢失,它只会把capacity置成size。
- vs下capacity是按1.5倍增长的,g++是按2倍增长的。vs是PJ版本STL,g++是SGI版本STL。
- reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
- resize在开空间的同时还会进行初始化,影响size。
vector的增删查改
函数接口 | 释义 |
---|---|
[ ] | 像数组一样下标访问 |
void push_back(const value_type& x); | 尾插数据x |
iterator insert (iterator position, const value_type& val); | 在pos的iterator位置之前插入x |
void pop_back() | 尾删 |
iterator erase (iterator pos); | 删除iterator的pos位置的元素 |
void swap (vector& x); | 交换两个vector的数据空间 |
InputIterator find (InputIterator first, InputIterator last, const T& val); | 迭代器区间内查找 |
find函数是算法库的函数,需要加头文件#include <algorithm> // std::find
迭代器失效
insert导致迭代器失效
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int a[] = { 1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 在pos位置插入数据,导致pos迭代器失效。
// insert会导致迭代器失效,是因为insert可
// 能会导致增容,增容后pos还指向原来的空间,而原来的空间已经释放了。
vector<int>::iterator it = find(v.begin(), v.end(), 3);
v.insert(it, 30);
cout << *it << endl; // 此处会导致非法访问
system("pause");
return 0;
}
erase导致迭代器失效
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int a[] = { 1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
//删除pos位置的数据,导致pos迭代器失效。
vector<int>::iterator it = find(v.begin(), v.end(), 3);
v.erase(it);
cout << *it << endl;// 此处会导致非法访问
system("pause");
return 0;
}
注意
函数 | 原因 | 返回值 |
---|---|---|
insert | 插入可能会增容,原来的数据空间已经被释放 | 返回插入数据的迭代器 |
erase | it位置的元素被删,it失效 | 删除数据的下一个元素的迭代器 |
vector的亲戚
array和vector的比较
不同点:
- array是固定大小的线性存储元素。由于其固定大小的特点,不会有空间浪费的问题。
template < class T, size_t N > class array;//定义时候就需要给定大小
-
array是固定大小的数组,因此array没有push_back、popback、insert、erase、resize、reserve这样改变空间大小的接口,array只能通过下标访问,array就是我们最熟悉的C语言的数组。
-
array只有
max_size()
,表示容器最多可以容纳的元素个数,没有capacity()
,而关于vector的max_size和capacity的区别。
vector的capacity是分配到的空间大小,如果这个vector没有插入数据,那么capacity等于size。一旦vector开始插入数据,随之会有相应的增容。而max_size是vector最大潜在大小,受限制与系统和库。 -
array有
void fill (const value_type& val);
可以用来初始化array,Fill array with value。
#include<iostream>
#include<array>
using namespace std;
int main()
{
array<int, 10> arr;
arr.fill(10);
for (size_t i = 0; i < arr.size(); i++)
cout << arr[i] << endl;
system("pause");
}
- array没有构造函数和析构函数,array在定义时候就开辟了固定空间,vector在构造函数时候才开辟空间。
相同点
- 存在插入和删除在非尾部,效率低下,会存在元素的移动,swap效率低下。
- array和vector都有front和back这样直接返回首元素和尾元素的引用。
at()
都会实时监测是否越界,[ ]
不会监测,都是返回该位置元素的引用。
柔性数组
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
typedef struct st_type
{
int i;
int a[0]; //柔性数组成员,也可以使用a[]
}type_a;
柔性数组的特点:
- 结构中的柔性数组成员前面必须至少一个其他成员。
- sizeof 返回的这种结构大小不包括柔性数组的内存。
- 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
#include<iostream>
#pragma warning(disable:4200)//使用柔性数组会发出警告,或者将柔性数组元素个数设置为1
using namespace std;
typedef struct st_type
{
int i;
int a[0]; //柔性数组成员,或者使用a[]
}type_a;
int main()
{
int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100 * sizeof(int));
//业务处理
p->i = 100;
for (i = 0; i<100; i++)
{
p->a[i] = i;
}
free(p);
system("pause");
return 0;
}
相比结构体中存放一个数组的指针,柔性数组的优势
- 增加访问速度,结构体和数组的空间连在一起,访问效率会高一些。
- 一次申请空间,一次释放空间。如果采用结构体中存数组的指针的方法,需要分别申请结构体和数组的空间,释放空间也要分两次释放。