重点总结
Vector
采用连续存储结构,每个元素在内存上是连续的,默认容量是0。Vector
内存空间不够时,会重新申请一块足够大的内存,把旧内存空间中的所有元素都拷贝进新内存空间中去,同时释放旧的空间。GCC是二倍扩容,VS13是1.5倍扩容。Vector
能非常好的支持随机存取,即使用 []操作符 或 at() 访问其中元素,复杂度O(1)。- [插入删除] 当Vector 无需扩容时,插入insert() 和删除 earse() 复杂度是O(n),push_back(), pop_back() 复杂度为O(1).
- [优缺点] 高效的随机访问。 对于删除或插入操作,执行效率不高,越靠后插入或删除执行效率越高。
概述
概述-1. vector底层原理
STL
众多容器中,vector
是最常用的容器之一,其底层所采用的数据结构非常简单,就只是一段连续的线性内存空间。
std::vector<int> vec = {1,2,3,4,5};
-
对象
vec
存储在栈
中,对象中的成员指针指向堆
上的地址。
-
通过分析
vector
源码,vector
包 3 个迭代器(可以理解成指针)成员:
//_Alloc 表示内存分配器,此参数几乎不需要我们关心
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector{
...
protected:
pointer _Myfirst;
pointer _Mylast;
pointer _Myend; // 迭代器可以理解为指针
};
- 其中,
_Myfirst
指向的是堆
中开辟内存处起始位置(第一个元素);_Mylast
指向当前最后一个元素的位置;_myend
指向整个容器内存空间的后一个地址。
概述-2. 基础功能
assign 修改容器中内容
at 返回索引处元素的引用
back 返回指向容器尾元素的迭代器
begin 返回指向容器头的迭代器
capacity 返回容器分配的空间大小
cbegin 返回指向容器头的迭代器-const
cend 返回指向容器尾元素后一个位置的迭代器 - const
clear 清空容器
crbegin 返回指向容器最后一个元素的 逆序 迭代器 - const
crend 返回指向容器头元素前一个位置的 逆序 迭代器 - const
data 返回指向容器底层中内存数组的指针
emplace 通过迭代器在指定位置插入新元素,move
emplace_back 在容器尾处插入元素新元素,move
empty 判断容器是否为空
end 返回指向容器尾的迭代器
erase 删除元素
front 返回容器头元素的引用
get_allocator
insert 插入元素
max_size 返回容器可以容纳的最大元素数
operator= 重载运算符 =
operator[] 重载运算符 [],通过索引可返回对应元素的引用
pop_back 删除容器的最后一个元素
push_back 在容器尾处插入元素,copy
rbegin 返回指向容器最后一个元素的 逆序 迭代器
rend 返回指向容器头元素前一个位置的 逆序 迭代器
reserve 改变容器所分配空间的大小
resize 改变容器的大小,如果new_size大于当前容量,则填充默认值
shrink_to_fit
size 返回容器中元素个数
swap 当前vector与作为参数的vector交换元素
1、构造函数、assign()
- 头文件
#include<vector>
- <1>. 构造函数,函数原型如下:
// 1. 创建一个空vector
explicit vector(const allocator_type& alloc = allocator_type());
// 2. 创建一个包含n个value(或元素默认值)的vector
explicit vector(size_type n);
vector(size_type n, const value_type& val,
const allocator_type& alloc = allocator_type());
// 3. 通过迭代器创建vector
template <class InputIterator>
vector(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
// 4. 复制构造
vector(const vector& x);
vector(const vector& x, const allocator_type& alloc);
// 5. 移动构造
vector(vector&& x);
vector(vector&& x, const allocator_type& alloc);
// 6. 通过 initializer list 构造
vector (initializer_list<value_type> il,
const allocator_type& alloc = allocator_type());
例: 与上述顺序相同
int main () {
// constructors used in the same order as described above:
std::vector<int> first; // [1]. empty vector of ints
std::vector<int> second(4,100); // [2]. four ints with value 100
std::vector<int> third(second.begin(),second.end()); // [3]. iterating through second
std::vector<int> fourth(third); // [4]. a copy of third
std::vector<int> fifth(std::move(third)); // [5]. a move of third
std::vector<int> sixth{1,2,3,4,5}; // [6].
std::cout << "The contents of fifth are:";
for (std::vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
打印结果:
The contents of fifth are: 100 100 100 100
- <2>.
assign()
,使用新元素替容器中当前元素,并修改容器大小。函数原型如下:
template <class InputIterator>
void assign(InputIterator first, InputIterator last); // 1. 半开区间[first,last),新元素按区间内顺序构造
void assign(size_type n, const value_type& val); // 2. 全部填充val
void assign(initializer_list<value_type> il); // 3. initializer list
例: 下列注释与上述序号相同
int main () {
std::vector<int> first;
std::vector<int> second;
std::vector<int> third;
std::vector<int> third_;
first.assign(7,100); // [2]. element: 7个100
second.assign(first.begin(), first.end()-1); // [1]. 通过迭代器assign, 5个元素
int myints[] = {1776,7,4};
third.assign(myints,myints+3); // [3]. 通过数组构造
third_.assign( {1,2,3,4,5,999} ); // [3]. 或这样
std::cout << "Size of first: " << int (first.size()) << std::endl;
std::cout << "Size of second: " << int (second.size()) << std::endl;
std::cout << "Size of third: " << int (third.size()) << std::endl;
std::cout << "Size of third_: " << int (third_.size()) << std::endl;
first.assign(3,1);
std::cout << "====" << std::endl; // **** 替换first 中之前的元素,size从7变成3
std::cout << "Size of first: " << int (first.size()) << std::endl;
return 0;
}
Size of first: 7
Size of second: 6
Size of third: 3
Size of third_: 6
====
Size of first: 3
2、operator[]、at()区别, data() 功能介绍
- <1>.
operator[]
与at()
- 相同点
1. 可以使用operator[]
orat()
方法来访问容器中元素,均返回对应元素引用。
2. 起始索引是0
,最大有效索引是vector.size()-1
。 - 区别
1.operator[]
不检查索引是否有效(原因是为了效率,总是强制下标越界检查会增加程序的性能开销。),遇到无效索引直接报错。
2.at()
会检查索引是否有效,在遇到无效索引时会抛出 out_of_range 异常。
- <2>.
data()
,返回指向容器底层中内存数组的指针,函数原型如下:
value_type* data() noexcept;
const value_type* data() const noexcept;
int main ()
{
std::vector<int> myvector(5); // int 默认值 0
int* p = myvector.data(); // 指向容器底层数组 中第一个元素, p可以理解为数组名,数组名也是数组第一个元素的地址
*p = 10; // [1]. 通过指针赋值
++p; // next element
*p = 20;
p[2] = 100; // [2]. 编译器将 p[index] 看做 *(p+index)
std::cout << "myvector contains:";
for (unsigned i=0; i<myvector.size(); ++i)
std::cout << ' ' << myvector[i];
std::cout << '\n';
return 0;
}
打印结果:myvector contains: 10 20 0 100 0
3、size, max_size, capacity, resize, reserve 功能介绍
size()
,返回容器当前元素个数。max_size()
,返回容器可以容纳的最大元素数。capacity()
,返回容器当前分配的空间大小,原型如下:
size_type size() const noexcept;
size_type max_size() const noexcept;
size_type capacity() const noexcept;
int main () {
std::vector<int> 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;
}
size: 100
capacity: 128
max_size: 1073741823
resize()
,调整容器的大小,使其包含n
个元素。- 若
n
小于当前容器的大小,则vec.size()
减小至n
,删除尾部超出范围的元素。 - 若
n
大于当前容器的大小,则在容器末尾处插入一定数量的元素,使vec.size()
为n
。 - 若参数包含
val
,插入的新元素为val
,否则,插入的新元素为类型默认值(如 int 0)。
void resize (size_type n);
void resize (size_type n, const value_type& val);
int main () {
std::vector<int> myvector;
// set some initial content:
for (int i=1; i<10; i++)
myvector.push_back(i); // 1,2,3,...,9
myvector.resize(5); // size=5, element: 1,2,3,4,5
myvector.resize(8,100); // size=8, 1,2,3,4,5,100,100,100
myvector.resize(12); // size=12, 1,2,3,4,5,100,100,100,0,0,0 int默认值为0
std::cout << "myvector contains:";
for (int i=0;i<myvector.size();i++)
std::cout << ' ' << myvector[i];
std::cout << '\n';
return 0;
}
myvector contains: 1 2 3 4 5 100 100 100 0 0 0 0
reserve(n)
,调整容器capacity
的大小。- 若
n
大于当前vec.capacity()
,则为容器重新分配存储空间,vec.capacity()
将扩大至n
或更大。 - 其他情况下,不会重新分配存储空间,
vec.capacity()
值不受影响。
void reserve (size_type n);
int main () {
std::vector<int>::size_type sz;
std::vector<int> foo;
sz = foo.capacity();
std::cout << "making foo grow:\n";
for (int i=0; i<100; ++i) {
foo.push_back(i);
if(sz!=foo.capacity()) {
sz = foo.capacity(); // 容量不够, VS-1.5倍扩容, gcc-2倍扩容
std::cout << "capacity changed: " << sz << '\n';
}
}
std::vector<int> bar;
sz = bar.capacity();
bar.reserve(100); // 使用reserve() 修改容器capacity 大小
std::cout << "making bar grow:\n";
for (int i=0; i<100; ++i) {
bar.push_back(i);
if (sz!=bar.capacity()) {
sz = bar.capacity();
std::cout << "capacity changed: " << sz << '\n';
}
}
return 0;
}
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128
making bar grow:
capacity changed: 100
4、insert() 功能介绍
- 函数原型如下:
接下来按顺序对上述功能进行介绍,首先,创建一个包含字符元素的vector
using namespace std;
int main(){
//创建一个vector,置入字母表的前十个字符
vector <char> vector_char;
for( int i=0; i < 5; i++ )
vector_char.push_back(i+65);
}
- <1>.
iterator insert (const_iterator position, const value_type& val);
<函数功能>. 在指定位置position 前插入值为 val 的元素,返回指向这个元素的迭代器。
// 续接前一节代码:
// ... 打印为 ZABCDE
vector_char.insert(vector_char.begin(),'Z');
for (auto i:vector_char) cout << i;
}
- <2>.
iterator insert (const_iterator position, size_type n, const value_type& val);
<函数功能>. 在指定位置position 前插入 n 个值为 val 的元素,返回一个迭代器。
// 续接前一节代码:
//... 打印结果为 WWZABCDE
vector_char.insert(vector_char.begin(),2,'W');
for (auto i:vector_char) cout << i;
}
- <3>.
iterator insert (const_iterator position, InputIterator first, InputIterator last);
<函数功能>. 在指定位置position 前插入 另一个vector 的 Iterator.first至Iterator.last的元素,返回一个迭代器。
// 续接前一节代码:
// ... 打印 WWZABCDE WWZABCDE
vector_char.insert(vector_char.end(),vector_char.begin(),vector_char.end());
for (auto i:vector_char) cout << i;
}
5、遍历方法与排序
- 三种方法遍历
vector
- [1]. 下标遍历
vector<int> arr{1,2,3,4,5};
for(int i=0; i<arr.size(); ++i)
cout << arr[i] << " ";
// or
cout << arr.at(i) << " ";
- [2]. 迭代器遍历
vector<int> arr{1,2,3,4,5};
for(vector<int>::iterator iter=arr.begin();it!=arr.end();it++)
cout << *iter << " ";
- [3]. C++11遍历
vector<int> arr{1,2,3,4,5};
for (auto& iter : arr)
cout << iter << " ";
- 正序排序, 顺序颠倒,逆序排序
// 使用头文件中算法 #include<algorithm>,
vector<int> arr{1,2,3,4,5,4,3,2,1,0};
// 正序排序
std::sort(arr.begin(),arr.end());
// 顺序颠倒
std::reverse(arr.begin(),arr.end());
// 逆序排序
std::sort(arr.rbegin(), arr.rend());
6、反向迭代器 rbegin(), rend()
rbegin(); rend()
返回逆序迭代器。crbegin(); crend()
返回const
修饰的逆序迭代器。
std::vector<int> vec;
vec.rbegin() // 返回一个逆序迭代器,它指向容器vec的最后一个元素
vec.rend() // 返回一个逆序迭代器,它指向容器vec的第一个元素前面的位置
vec.crbegin() // 返回一个逆序迭代器-const,它指向容器vec的最后一个元素
vec.crend() // 返回一个逆序迭代器-const,它指向容器vec的第一个元素前面的位置
- 如果容器是
const
的,则其返回类型要加上const_
前缀,也就是const_iterator
和const_reverse_iterator
类型。
- 反向迭代器是一种反向遍历容器的迭代器。通过对
++
和--
运算符进行重载,使得: - 对于反向迭代器,
++
运算将访问前一个元素,--
运算则访问下一个元素。
例1-逆序打印:
int main() {
std::vector<int> vec{0,1,2,3,4,5,6,7,8,9};
vector<int>::reverse_iterator r_iter;
for (r_iter = vec.rbegin(); // 迭代器指向最后一个元素
r_iter != vec.rend(); // rend() 指向第一个元素的前一个
++r_iter) // ++操作访问前一个元素
std::cout << *r_iter << " "; // prints 9,8,7,...0
std::cout << "\n";
return 0;
}
打印结果:9 8 7 6 5 4 3 2 1 0
- 为了以降序排列容器中元素,需向
std::sort
传递一对反向迭代器:
例2-降序排列:
int main() {
std::vector<int> vec{1,2,3,0,5};
// sorts vec in "normal" order, 0 1 2 3 5
std::sort(vec.begin(), vec.end());
std::cout << "正序排列: ";
for (auto& i:vec)
std::cout << i << " ";
// sorts in reverse: puts smallest element at the end of vec, 5 3 2 1 0
std::sort(vec.rbegin(), vec.rend());
std::cout << "\n逆序排列: ";
for (auto& i:vec)
std::cout << i << " ";
return 0;
}
正序排列: 0 1 2 3 5
逆序排列: 5 3 2 1 0