【C++】朝花夕拾——STL vector
STL之vector篇
N久之前是拿C的数组实现过vector中的一些简单功能,什么深拷贝、增删查找之类的,以为vector的实现也就是这样了,现在想想真是...too young too naive...ORZ
====================我是分割线=============================
vector属于顺序容器,它的底层实现就是基于array,所以它可以支持随机访问,但是它比array更有效率,因为它动态分配的内存空间。
动态分配的内存空间:
每当vector的capacity==size,并且有新element需要加入时,vector会申请一段大小等于2*capacity的连续内存空间,将原来的数据深拷贝到新的内存地址,然后释放掉原来的内存,并添加新的element到内存中。(这一系列内存操作大大影响了vector的存储效率)
因为,不同的环境下,vector的实现方式有所出入,所以初始化时的capacity大小不一样,但是capacity一定会大于所需的内存大小,预留一定的空间,已减少内存操作。
以下是简单的测试:
环境如下:
g++ 4.8.4
ubuntu 14.04
test1:
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl;
vector<char> v2;
cout << "v2 size = " << v2.size() << endl;
cout << "v2 capacity = " << v2.capacity() << endl;
cout << "v2 max_size = " << v2.max_size() << endl;
return ;
}
结果如下:
v1 size = 0
v1 capacity = 0
v1 max_size = 4611686018427387903
v2 size = 0
v2 capacity = 0
v2 max_size = 18446744073709551615
max_size返回vector可以保持的element个数的上限,这个上限与系统的底层实现有关,不一定可以达到(当内存不够时,不能继续分配)
这里可以看出,此时初始化的capacity==0
test2:
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl;
vector<char> v2;
for (int i = ; i < ; ++i)v2.push_back(char(i));
cout << "v2 size = " << v2.size() << endl;
cout << "v2 capacity = " << v2.capacity() << endl;
cout << "v2 max_size = " << v2.max_size() << endl;
return ;
}
结果如下:
v1 size = 1
v1 capacity = 1
v1 max_size = 4611686018427387903
v2 size = 1
v2 capacity = 1
v2 max_size = 18446744073709551615
此时,插入一个元素,capacity==1,size也为1
test3:
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl;
vector<char> v2;
for (int i = ; i < ; ++i)v2.push_back(char(i));
cout << "v2 size = " << v2.size() << endl;
cout << "v2 capacity = " << v2.capacity() << endl;
cout << "v2 max_size = " << v2.max_size() << endl;
return ;
}
结果如下:
v1 size = 2
v1 capacity = 2
v1 max_size = 4611686018427387903
v2 size = 2
v2 capacity = 2
v2 max_size = 18446744073709551615
当插入两个元素时,capacity = 2 * capacity(即为2),然后拷贝之前的数据到新的内存空间,添加新元素,则size==2
test4:
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl;
vector<char> v2;
for (int i = ; i < ; ++i)v2.push_back(char(i));
cout << "v2 size = " << v2.size() << endl;
cout << "v2 capacity = " << v2.capacity() << endl;
cout << "v2 max_size = " << v2.max_size() << endl;
return ;
}
结果如下:
v1 size = 3
v1 capacity = 4
v1 max_size = 4611686018427387903
v2 size = 3
v2 capacity = 4
v2 max_size = 18446744073709551615
又接着添加一个新元素,capacity翻倍(变成4),size增加1,即3
添加新元素的原理get > <
===========================第二分割线===================================
以下为删除原理
test5:
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl;
vector<char> v2;
for (int i = ; i < ; ++i)v2.push_back(char(i));
cout << "v2 size = " << v2.size() << endl;
cout << "v2 capacity = " << v2.capacity() << endl;
cout << "v2 max_size = " << v2.max_size() << endl;
return ;
}
结果如下:
v1 size = 129
v1 capacity = 256
v1 max_size = 4611686018427387903
v2 size = 129
v2 capacity = 256
v2 max_size = 18446744073709551615
test6:
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
v1.pop_back();
v1.pop_back();
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl;
vector<char> v2;
for (int i = ; i < ; ++i)v2.push_back(char(i));
v2.pop_back();
v2.pop_back();
cout << "v2 size = " << v2.size() << endl;
cout << "v2 capacity = " << v2.capacity() << endl;
cout << "v2 max_size = " << v2.max_size() << endl;
return ;
}
结果如下:
v1 size = 127
v1 capacity = 256
v1 max_size = 4611686018427387903
v2 size = 127
v2 capacity = 256
v2 max_size = 18446744073709551615
(⊙v⊙)嗯,看来内存的动态改变并不会随着size的减小的缩减capacity,避免了不必要的内存操作。
C++手册中也有说明,pop_back的操作只是size减1
=============================第三分割线==================================
接着,是各个函数的底层理解
assign()
有三种形参形式(iterator begin,iterator end)初始化为迭代器指向的容器之间的内容;(n, value)初始化N个值为value的元素;(initializer_list<value_type> il)深拷贝
back()
返回vector中最后一个值,当vector为空时,调用该函数会报错
front()
返回vector中第一个值,当vector为空时,调用该函数会报错
begin()
返回指向第一个element的迭代器
end()
返回指向past-the-end element的迭代器,past-the-end element是理论上应该跟在最后一个element后面的位置,但是那个位置上并没有存放element
cbegin()
返回一个首个元素的const_iterator迭代器,用该迭代器访问元素是是不可以用来改变元素本身值的(即便元素本身并非const类型)
cend()
返回一个past-the-last element的const_iterator迭代器
crbegin()
r表示逆序,c表示const,此处返回vector中逆序第一个element的const_iterator
crend()
此处返回vector中第一个element的const_iterator,功能与cbegin()相同
rbegin()
返回逆序第一个element的迭代器,即为指向end()的前一个位置的迭代器
rend()
返回逆序的最后一个element的迭代器,即指向第一个element
capacity()
返回vector容器已开辟的内存可存放element的个数
size()
返回当前vector中已有的element个数,size <= capacity
max_size()
返回一个理论上允许的capacity上限值,但不一定能达到
data()
返回一个指向当前容器内存起始地址的指针,由于vector的内存分配是连续的,所以可以直接用指针offset来为容器中的element赋值
std::vector<int> myvector (5);
int* p = myvector.data();
*p = 10;
++p;
*p = 20;
p[2] = 100;
emplace()
关于emplace和insert的区别,如下:
(emplace会调用类的构造函数,代码搬运自:http://stackoverflow.com/questions/14788261/c-stdvector-emplace-vs-insert)
struct Foo
{
Foo(int n, double x);
}; std::vector<Foo> v;
v.emplace(someIterator, , 3.1416);
v.insert(someIterator, Foo(, 3.1416));
emplace_back()
调用构造函数,然后把element加到末尾
insert()
可以把element插入到迭代器指定的element之前的位置。
由于底层实现时array,除了在末尾插入element,在其他的位置插入,都需要将指定插入位置后面的element移位,这将导致效率低下
如果插入之后size>capacity,则参考push_back的重新申请内存空间
erase()
删除指定区间或者位置的element,同样,除了删除末尾的element,删除其他位置的element都需要把后面的element往前移动,也是导致效率低的原因
clear()
把所有element都移除,size=0,此时capacity不一定为0(不一定会有释放内存的操作)//正确的释放内存姿势——swap()
empty()
size >= 0, 返回false;否则返回true
get_allocator()
返回该容器的allocator
operator=
拷贝,STL中的拷贝都是深拷贝
operator[]
访问节点
push_back()
在末尾增加一个element,当size超出时,capacity翻倍(具体看此博文开头),size++
pop_back()
移除末尾一个element, size--
reserve()
v.reserve(n);表示此时V的capacity至少要大于等于300,若capacity<300,则重新申请内存;反之则不用做其他操作;
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
v1.pop_back();
v1.pop_back();
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl;
vector<char> v2;
for (int i = ; i < ; ++i)v2.push_back(char(i));
v2.reserve();
cout << "v2 size = " << v2.size() << endl;
cout << "v2 capacity = " << v2.capacity() << endl;
cout << "v2 max_size = " << v2.max_size() << endl;
return ;
}
结果为:
v1 size = 127
v1 capacity = 256
v1 max_size = 4611686018427387903
v2 size = 129
v2 capacity = 300
v2 max_size = 18446744073709551615
resize()
v.resize(n)将会把v中的size调整为n大小,若n > size,则用0补足element;若n < size,则取容器中的前n个element,其与元素移除;
v.resize(n,m)将会把v中的size调整为n大小,若n > size,则用m补足element
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
for (int i = ; i < v1.size(); i ++)cout << v1[i] << " ";
cout << endl;
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl; v1.resize();
for (int i = ; i < v1.size(); i ++)cout << v1[i] << " ";
cout << endl;
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl; v1.resize(, );
for (int i = ; i < v1.size(); i ++)cout << v1[i] << " ";
cout << endl;
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl; v1.resize();
for (int i = ; i < v1.size(); i ++)cout << v1[i] << " ";
cout << endl;
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl;
return ;
}
结果如下:
0 1 2 3 4 5 6 7 8 9
v1 size = 10
v1 capacity = 16
v1 max_size = 4611686018427387903
0 1 2 3 4 5 6
v1 size = 7
v1 capacity = 16
v1 max_size = 4611686018427387903
0 1 2 3 4 5 6 200 200 200 200 200 200 200 200 200 200 200 200 200
v1 size = 20
v1 capacity = 20
v1 max_size = 4611686018427387903
0 1 2 3 4 5 6 200 200 200 200 200 200 200 200 200 200 200 200 200 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
v1 size = 50
v1 capacity = 50
v1 max_size = 4611686018427387903
shrink_to_fit()
一般来说capacity满足,capacity>=size;v.shrink_to_fit()操作可以使得,capacity==size,这也是释放内存的操作之一!!!
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl; v1.resize();
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl; v1.shrink_to_fit();
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl; return ;
}
结果如下:
v1 size = 10
v1 capacity = 16
v1 max_size = 4611686018427387903
v1 size = 7
v1 capacity = 16
v1 max_size = 4611686018427387903
v1 size = 7
v1 capacity = 7
v1 max_size = 4611686018427387903
swap()
v.swap(v2)的操作可以将v,和v2中的element交换
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl; vector<int> v2;
for (int i = ; i < ; ++i)v2.push_back(i);
cout << "v2 size = " << v2.size() << endl;
cout << "v2 capacity = " << v2.capacity() << endl;
cout << "v2 max_size = " << v2.max_size() << endl; v1.swap(v2);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl;
cout << "v1 max_size = " << v1.max_size() << endl;
cout << "v2 size = " << v2.size() << endl;
cout << "v2 capacity = " << v2.capacity() << endl;
cout << "v2 max_size = " << v2.max_size() << endl; return ;
}
结果如下:
v1 size = 10
v1 capacity = 16
v1 max_size = 4611686018427387903
v2 size = 20
v2 capacity = 32
v2 max_size = 4611686018427387903
v1 size = 20
v1 capacity = 32
v1 max_size = 4611686018427387903
v2 size = 10
v2 capacity = 16
v2 max_size = 4611686018427387903
小结:
vector是不会自动释放内存的,但是如果size的极大值很大的话会造成capacity很大,浪费内存。
所以以下有几种方法可以释放vector的内存:
①shrink_to_fit
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl; v1.clear();
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl; v1.shrink_to_fit();
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl; return ;
}
结果:
v1 size = 20
v1 capacity = 32
v1 size = 0
v1 capacity = 32
v1 size = 0
v1 capacity = 0
②swap
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v1;
for (int i = ; i < ; ++i)v1.push_back(i);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl; vector<int>().swap(v1);
cout << "v1 size = " << v1.size() << endl;
cout << "v1 capacity = " << v1.capacity() << endl; return ;
}
结果如下:
v1 size = 20
v1 capacity = 32
v1 size = 0
v1 capacity = 0
③指针释放
#include <iostream>
#include <vector> using namespace std; int main() {
vector<int> v;
vector<int>* v1 = &v;
for (int i = ; i < ; ++i)v1->push_back(i);
cout << "v size = " << v.size() << endl;
cout << "v capacity = " << v.capacity() << endl; delete v1;
return ;
}
========================我是第四分割线============================
C++11的标准中,vector新增加的成员函数:
crbegin
crend
cbegin
cend
emplace
emplace_back
data
shrink_to_fit
========================我是第五分割线============================
最后简单说说,高效索引之bitset和vector<bool>
bitset 可以进行位操作,但是大小固定
vector<bool> 属于vector的一种特殊形式,用bool类型的allocator构造,但是在实现的时候优化了,所以它的element类型并不是bool,而是bit
【C++】朝花夕拾——STL vector的更多相关文章
- C++ STL vector容器学习
STL(Standard Template Library)标准模板库是C++最重要的组成部分,它提供了一组表示容器.迭代器.函数对象和算法的模板.其中容器是存储类型相同的数据的结构(如vector, ...
- STL vector
STL vector vector是线性容器,它的元素严格的按照线性序列排序,和动态数组很相似,和数组一样,它的元素存储在一块连续的存储空间中,这也意味着我们不仅可以使用迭代器(iterator)访问 ...
- STL vector用法介绍
STL vector用法介绍 介绍 这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作.本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if()和f ...
- STL vector+sort排序和multiset/multimap排序比较
由 www.169it.com 搜集整理 在C++的STL库中,要实现排序可以通过将所有元素保存到vector中,然后通过sort算法来排序,也可以通过multimap实现在插入元素的时候进行排序.在 ...
- STL vector 用法介绍
介绍 这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作.本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if()和for_each()中的使用.通 ...
- STL vector使用方法介绍
介绍 这篇文章的目的是为了介绍std::vector,怎样恰当地使用它们的成员函数等操作.本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if()和for_each()中的使用.通 ...
- stl——vector详解
stl——vector详解 stl——vector是应用最广泛的一种容器,类似于array,都将数据存储于连续空间中,支持随机访问.相对于array,vector对空间应用十分方便.高效,迭代器使ve ...
- C++STL vector详解(杂谈)
介绍 这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作.本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if()和for_each()中的使用.通 ...
- C++ stl vector介绍
转自: STL vector用法介绍 介绍 这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作.本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if ...
随机推荐
- SAP事务码 一
SE80 -- edit source code. SE24 -- class create or display. SFP -- created and maintained independent ...
- XMU 1050 Diffuse Secret 【最短路】
1050: Diffuse Secret Time Limit: 500 MS Memory Limit: 64 MBSubmit: 10 Solved: 8[Submit][Status][We ...
- YTU 1075: Time
1075: Time 时间限制: 1 Sec 内存限制: 128 MB 提交: 7 解决: 7 [提交][状态][讨论版] 题目描述 Digital clock use 4 digits to e ...
- 《Visual C++ 2010入门教程》系列二:安装、配置和首次使用VS2010
作者:董波 日期:2010.6.15 写在前面 在我还在上学的时候,我选择了C++,最初我用VC6作为我的IDE,我看过很多本C++的教材,有的适合我,有的不适合我,其中有一本叫<Visual ...
- Jquery与Dom对象相互转化
Jquery与Dom对象相互转化 jQurey对象和DOM对象的相互转换 在讨论jQurey对象和DOM对象的相互转换之前,我们先约定定义变量的风格.如果我们获取的对象是jQuery对象,那么我们 ...
- ”吐槽“qemu的块设备驱动
花点时间来总结一下前阵子的工作. qemu的底层块设备无疑是我所见过的最复杂的模块了,说得好像我很精通很多模块一样(大雾). 它的raw镜像格式文件的驱动的核心代码主要都是在raw-posix.c文件 ...
- 并不对劲的bzoj4817:loj2001:p3703:[SDOI2017]树点涂色
题目大意 有一棵\(n\)(\(n\leq10^5\))个节点的树,每个点有颜色\(c\),一开始所有颜色互不相同 要进行\(m\)(\(m\leq10^5\))次操作,每次操作是以下三种中的一种: ...
- bzoj 1504 郁闷的出纳员
题目大意: 有一些员工 他们有工资 当他们的工资低于一个值时 他们会永远离开 I命令 I_k 新建一个工资档案,初始工资为k. 如果某员工的初始工资低于工资下界,他将立 ...
- Linux 常用命令十六 文件权限管理
一.ls -l 各段含义 wang@wang:~/workpalce/threading$ ls -l 总用量 drwxrwxr-x wang wang 12月 : a -rw-rw-r-- wang ...
- 强连通分量初探 By cellur925
并不理解.但是毕竟也做了一些题,略微小结. 注:这里讨论的暂时是有向图的强联通分量. 先贴出模板.学长:我也不理解,但我可以叫你们怎么背代码. #include<cstdio> #incl ...