vector概述
vector是一个能够支持任何类型的容器,本身为一个可以动态增长的数组。
1、vector基本数据结构
STL中所有的容器都包括三部分:
- 迭代器,遍历容器的元素,控制容器空间的边界和元素移动。
- 构造函数,满足容器多种多样的初始化。
- 属性获取,比如begin(),end()等。
template <class T, class Alloc = alloc>
class vector {
public:
// 定义 vector 自身的嵌套型别
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
// 定义迭代器, 这里就只是一个普通的指针
typedef value_type* iterator;
typedef const value_type* const_iterator;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
...
protected:
typedef simple_alloc<value_type, Alloc> data_allocator; // 设置其空间配置器
iterator start; // 当前使用空间的头
iterator finish; // 当前使用空间的尾
iterator end_of_storage; // 当前可用空间的尾
...
};
因为 vector 需要表示用户操作的当前数据的起始地址,结束地址,还需要其真正的最大地址。所以总共需要 3 个迭代器,分别来指向数据的头(start),数据的尾(finish),数组的尾(end_of_storage)。
2、构造函数
vector是一种class template, 并不需要手动的释放内存,生命周期结束后就自动调用析构从而释放调用空间。
void deallocate() {
if (start)
data_allocator::deallocate(start, end_of_storage - start);
}
// 调用析构函数并释放内存
~vector() {
destroy(start, finish);
deallocate();
}
3、属性获取
比如返回 vector 的开始和结尾,返回最后一个元素,返回当前元素个数,元素容量,是否为空等。这里需要注意的是因为 end() 返回的是 finish,而 finish 是指向最后一个元素的后一个位置的指针,所以使用 end() 的时候要注意。
public:
// 获取数据的开始以及结束位置的指针. 记住这里返回的是迭代器, 也就是 vector 迭代器就是该类型的指针.
iterator begin() { return start; }
iterator end() { return finish; }
reference front() { return *begin(); } // 获取值
reference back() { return *(end() - 1); }
...
size_type size() const { return size_type(end() - begin()); } // 数组元素的个数
size_type max_size() const { return size_type(-1) / sizeof(T); } // 最大能存储的元素个数
size_type capacity() const { return size_type(end_of_storage - begin()); } // 数组的实际大小
bool empty() const { return begin() == end(); }
//判断 vector 是否为空, 并不是比较元素为 0,是直接比较头尾指针。
4、push和pop操作
vector 的 push 和 pop 操作都只是对尾进行操作, 这里说的尾部是指数据的尾部。当调用 push_back 插入新元素的时候,首先会检查是否有备用空间,如果有就直接在备用空间上构造元素,并调整迭代器 finish。
如果没有备用空间,就扩充空间(重新配置-移动数据-释放原空间),这里实际是调用了另外一个函数:insert_aux 函数。
上图中显示push_back这个函数判断了一次 finish != end_of_storage 这是原因是因为 insert_aux 函数可能还被其他函数调用。
在else 分支进行vector 的动态扩容机制:如果原空间大小为 0 则分配 1 个元素,如果大于0则分配原空间两倍的新空间,然后把数据拷贝过去。
5、pop 元素:
从尾端删除一个元素。
void pop_back()
{ // erase element at end
erase(end() - 1);
}
6、erase 删除元素
erase 函数清除指定位置的元素, 其重载函数用于清除一个范围内的所有元素。实际上就是将删除元素后面所有元素往前移动,对于 vector 来说删除元素的操作开销很大,所以说 vector 它不适合频繁的删除操作,因为它本身是一个数组。示意图如下所示:
清除指定范围内的元素,首先将 finish 迭代器后面的元素拷贝回去,然后返回拷贝完成的尾部迭代器,最后再销毁之后的元素。
删除指定位置的元素就是实际就是将指定位置后面的所有元素向前移动, 最后析构掉最后一个元素。
7、insert 元素
- 插入点之后的现有元素个数 > 新增元素个数
- 插入点之后的现有元素个数 <= 新增元素个数
- 如果备用空间不足
注意:
vector有一个迭代失效问题。所谓迭代失效时由于元素空间重新分配导致之前迭代器访问的元素不存在了,总体有两种情况:
- 由于插入元素,使得容器元素整体迁移导致存放原容器元素的空间不再有效,从而使得指向原空间的迭代器失效。
- 由于删除元素,使得某些元素次序发生改变导致原本指向某元素的迭代器不再指向期望指向的元素。
全局函数说明:
- copy(a,b,c):将(a,b)之间的元素拷贝到(c,c-(b-a))位置。
- uninitialized_copy(first, last, result):具体作用是将 [first,last)内的元素拷贝到 result 从前往后拷贝。
- copy_backward(first, last, result):将 [first,last)内的元素拷贝到 result 从后往前拷贝。
8、vector总结
- vector的成员函数都不做边界检查(at方法会抛异常),使用者要自己确保迭代器和索引值的合法性。
- 优点
- 在内存中占用一块联系的内存空间存储数据,可以像数组一样操作,并且支持动态扩容。
- 正因如此,其元素可以随机访问,支持下标和vector.at()操作。
- 节省空间。
- 缺点
- 由于其顺序存储的特性,vector插入、删除操作的时间复杂度是Q(n)。
- 只能在末端进行pop和push操作。
- 当动态长度超过默认分配大小后,需要整体重新分配、拷贝和释放空间。
vector概述的更多相关文章
- STL源码剖析读书笔记之vector
STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...
- 带你深入理解STL之Vector容器
C++内置了数组的类型,在使用数组的时候,必须指定数组的长度,一旦配置了就不能改变了,通常我们的做法是:尽量配置一个大的空间,以免不够用,这样做的缺点是比较浪费空间,预估空间不当会引起很多不便. ST ...
- vector源码1(参考STL源码--侯捷):源码
vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...
- Java集合框架之Vector浅析
Java集合框架之Vector浅析 一.Vector概述: 位于java.util包下的Vector是Java集合框架的重要一员,虽然没有ArrayList那么的常用,但是我们还要对其做相关学习: 1 ...
- java源码--Vector和Stack
一.Vector简介 1.1.Vector概述 通过API中可以知道: 1)Vector是一个可变化长度的数组 2)Vector增加长度通过的是capacity和capacityIncrement这两 ...
- 【c++】标准模板库STL入门简介与常见用法
一.STL简介 1.什么是STL STL(Standard Template Library)标准模板库,主要由容器.迭代器.算法.函数对象.内存分配器和适配器六大部分组成.STL已是标准C++的一部 ...
- STL源码剖析读书笔记--第四章--序列式容器
1.什么是序列式容器?什么是关联式容器? 书上给出的解释是,序列式容器中的元素是可序的(可理解为可以按序索引,不管这个索引是像数组一样的随机索引,还是像链表一样的顺序索引),但是元素值在索引顺序的方向 ...
- Java集合源码分析(三)Vevtor和Stack
前言 前面写了一篇关于的是LinkedList的除了它的数据结构稍微有一点复杂之外,其他的都很好理解的.这一篇讲的可能大家在开发中很少去用到.但是有的时候也可能是会用到的! 注意在学习这一篇之前,需要 ...
- STL——序列式容器
一.容器概述与分类 1. STL容器即是将运用最广的一些数据结构实现出来.常用的数据结构有array, list, tree, stack, queue, hash table, set, map…… ...
随机推荐
- hdu-3833 YY's new problem(数组标记)
http://acm.hdu.edu.cn/showproblem.php?pid=3833 做这题时是因为我在网上找杭电的数论题然后看到说这道题是数论题就点开看了以下. 然后去杭电上做,暴力,超时了 ...
- Codeforces 450C:Jzzhu and Chocolate(贪心)
C. Jzzhu and Chocolate time limit per test: 1 seconds memory limit per test: 256 megabytes input: st ...
- Java——HashMap集合详解
第一章 HashMap集合简介 1.1 介绍 HashMap基于哈希表的Map接口实现,是以key-value存储形式存在,即主要用来存放键值对.HashMap 的实现不是同步的,这意味着它不是线程安 ...
- Api接口测试总是似懂非懂,只因这个原理没搞清楚
前言 掌握了http协议,就掌握了接口测试 笔者在网络上看过不少接口测试教程,一上来就开始讲怎么操作工具,而不告诉读者为什么要这么操作.读者可能照猫画虎成功了,也可能操作失败了但不知为何出错. 因 ...
- CSS基础-7 盒子模型大小
在盒子模型当中,有些元素是影响盒子大小的. 首先border就会影响盒子的大小. 其次padding也会影响盒子的大小. 例子: .one { width:100px; height:100px; b ...
- 第三代微服务架构:基于 Go 的博客微服务实战案例,支持分布式事务
这是一个可一键部署在 Kubernetes-Istio 集群中的,基于 Golang 的博客微服务 Demo,支持分布式事务. 项目地址:https://github.com/jxlwqq/blog- ...
- 分享一篇:sql语句中使用子查询,可能会引起查询的性能问题,查询时间会变长
前段时间,做自动化适配的时候,查找需要的数据的时候,使用到了dblink,跨数据库实例进行访问,整段sql拼接再加上dblink,在plsql查询的时候,性能还不是很长时间,最多2分钟可以查到,前期调 ...
- 初识python: 斐波拉契数(生成器获取)
使用 生成器(yield) 获取斐波拉契数. 代码如下: def fun(n): a,b,c = 0,0,1 while a < n: yield b # b, c = c, b + c 以下 ...
- 初识python 之 mysql数据库基本操作
import pymysql 注:所有插入.修改.删除操作都必须要提交(conn.commit()) 连接数据库: conn = pymysql.connect( host = '127.0.0.1' ...
- unittest_assert断言(4)
测试用例是否测试通过是通过将预期结果与实际结果做比较来判定的,那代码中怎么来判定用例是否通过呢?在python中这种判定的方法就叫做断言,断言可以使用python的assert方法,也可以使用unit ...