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有一个迭代失效问题。所谓迭代失效时由于元素空间重新分配导致之前迭代器访问的元素不存在了,总体有两种情况:

  1. 由于插入元素,使得容器元素整体迁移导致存放原容器元素的空间不再有效,从而使得指向原空间的迭代器失效。
  2. 由于删除元素,使得某些元素次序发生改变导致原本指向某元素的迭代器不再指向期望指向的元素。

全局函数说明:

  1. copy(a,b,c):将(a,b)之间的元素拷贝到(c,c-(b-a))位置。
  2. uninitialized_copy(first, last, result):具体作用是将 [first,last)内的元素拷贝到 result 从前往后拷贝。
  3. copy_backward(first, last, result):将 [first,last)内的元素拷贝到 result 从后往前拷贝。

8、vector总结

  1. vector的成员函数都不做边界检查(at方法会抛异常),使用者要自己确保迭代器和索引值的合法性。
  2. 优点
    • 在内存中占用一块联系的内存空间存储数据,可以像数组一样操作,并且支持动态扩容。
    • 正因如此,其元素可以随机访问,支持下标和vector.at()操作。
    • 节省空间。
  3. 缺点
    • 由于其顺序存储的特性,vector插入、删除操作的时间复杂度是Q(n)。
    • 只能在末端进行pop和push操作。
    • 当动态长度超过默认分配大小后,需要整体重新分配、拷贝和释放空间。

vector概述的更多相关文章

  1. STL源码剖析读书笔记之vector

    STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...

  2. 带你深入理解STL之Vector容器

    C++内置了数组的类型,在使用数组的时候,必须指定数组的长度,一旦配置了就不能改变了,通常我们的做法是:尽量配置一个大的空间,以免不够用,这样做的缺点是比较浪费空间,预估空间不当会引起很多不便. ST ...

  3. vector源码1(参考STL源码--侯捷):源码

    vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...

  4. Java集合框架之Vector浅析

    Java集合框架之Vector浅析 一.Vector概述: 位于java.util包下的Vector是Java集合框架的重要一员,虽然没有ArrayList那么的常用,但是我们还要对其做相关学习: 1 ...

  5. java源码--Vector和Stack

    一.Vector简介 1.1.Vector概述 通过API中可以知道: 1)Vector是一个可变化长度的数组 2)Vector增加长度通过的是capacity和capacityIncrement这两 ...

  6. 【c++】标准模板库STL入门简介与常见用法

    一.STL简介 1.什么是STL STL(Standard Template Library)标准模板库,主要由容器.迭代器.算法.函数对象.内存分配器和适配器六大部分组成.STL已是标准C++的一部 ...

  7. STL源码剖析读书笔记--第四章--序列式容器

    1.什么是序列式容器?什么是关联式容器? 书上给出的解释是,序列式容器中的元素是可序的(可理解为可以按序索引,不管这个索引是像数组一样的随机索引,还是像链表一样的顺序索引),但是元素值在索引顺序的方向 ...

  8. Java集合源码分析(三)Vevtor和Stack

    前言 前面写了一篇关于的是LinkedList的除了它的数据结构稍微有一点复杂之外,其他的都很好理解的.这一篇讲的可能大家在开发中很少去用到.但是有的时候也可能是会用到的! 注意在学习这一篇之前,需要 ...

  9. STL——序列式容器

    一.容器概述与分类 1. STL容器即是将运用最广的一些数据结构实现出来.常用的数据结构有array, list, tree, stack, queue, hash table, set, map…… ...

随机推荐

  1. 【LeetCode】897. Increasing Order Search Tree 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 重建二叉树 数组保存节点 中序遍历时修改指针 参考资 ...

  2. 【LeetCode】859. Buddy Strings 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 日期 题目地址:https://leetcod ...

  3. 【JAVA今法修真】 第七章 洞天风云起,索引混乱平

    您好,我是南橘,万法仙门的掌门,刚刚从九州世界穿越到地球,因为时空乱流的影响导致我的法力全失,现在不得不通过这个平台向广大修真天才们借去力量.你们的每一个点赞,每一个关注都是让我回到九州世界的助力,兄 ...

  4. 初识python 之 爬虫:BeautifulSoup 的 find、find_all、select 方法

    from bs4 import BeautifulSoup lxml 以lxml形式解析html,例:BeautifulSoup(html,'lxml') #  注:html5lib 容错率最高fin ...

  5. 细谈 Java 匿名内部类 【分别 使用 接口 和 抽象类实现】

    1.前言 匿名内部类是什么东西? 没有名字的内部类就是匿名内部类. 什么场景使用? 匿名内部类适合创建那种只需要一次使用的类. 这是个很有用的东西,可想而知,如果不使用匿名内部类,哪些只需要使用一次的 ...

  6. C语言考题:输入一个字符串,将此字符串中特定的字符删去后, 显示新的字符串,要求用函数来完成删去字符的操作。

    #include <stdio.h> #include <string.h> /*此题只需要删除单个字符,比较简单.相信大家也能做出来的.我这个也是可以实现的.只是加了两个判断 ...

  7. Python常用功能函数系列总结(七)

    本节目录 常用函数一:批量文件重命名 常用函数一:批量文件重命名 # -*- coding: utf-8 -*- """ DateTime : 2021/02/08 10 ...

  8. 百度地图BMap实现在行政区域内做标注

    使用环境 vue bmap.js element-ui 页面展示 前提步骤 在index中引入百度地图提供的js库 在使用的vue页面中实例化地图 <!-- 给id随便起给名字 --> & ...

  9. Chromium Windows Build

    https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/windows_build_instructions.md ...

  10. JS里默认和常用转换

    * { font-family: PingFang, Monaco } JS里的六大简单数据类型 string 字符类型 number 数字类型 boolean 布尔类型 symbol ES6语法新增 ...