当前位置: 首页 > ds >正文

C++ -- vector

vector

  • 1. 关于vector
    • 1.1 对比原生数组
    • 1.2 vector的核心优势
  • 2. 扩容
    • 2.1 底层实现
    • 2.2 扩容过程
  • 3. 构造函数
  • 4. 接口模拟实现
    • 4.1 实现迭代器
    • 4.2 扩容
    • 4.3 重载[]
    • 4.4 插入和删除
    • 4.5 构造函数和析构函数
  • 5. 迭代器失效
    • 5.1 扩容后失效
    • 5.2 越界失效
  • 6. 深浅拷贝

1. 关于vector

1.1 对比原生数组

  1. 固定大小,缺乏动态扩展能力
    • 原生数组需在编译时确定大小
    • vector可根据需求动态扩容
  2. 安全性问题
    • 原生数组越界访问会导致未定义行为,甚至程序崩溃
    • vector提供边界检查
  3. 功能缺失
    • 原生数组不支持直接插入、删除或动态调整
    • vector提供丰富接口

1.2 vector的核心优势

  1. 动态数组特性:自动管理内存,无需手动new/delete;支持动态扩容
  2. 连续内存布局:数据在内存中连续存储,可通过下标直接定位元素
  3. 高性能操作:尾部插入/删除均摊O(1)时间复杂度

2. 扩容

2.1 底层实现

在这里插入图片描述
在vector上有三个迭代器:_start指向头部,_finish指向有效数据的下一个位置,_endofstorage指向存储容量的尾

2.2 扩容过程

在插入数据时,是否扩容的前提是_finish是否等于_endofstorage,如果是则会进行扩容。在这里插入图片描述
扩容时,编译器会开辟一块新空间,将原有数据拷贝到新空间上,并将迭代器指向新的空间,最后释放掉旧空间。
在这里插入图片描述

3. 构造函数

vector的构造函数有这几种
在这里插入图片描述

int main()
{vector<int> v1;vector<int> v2(10, 0);// 创建n个,并给其初始值string str = "hello world";vector<int> v3(str.begin(), str.end());// 用迭代器vector<int> v4(v3);// 拷贝构造return 0;
}

4. 接口模拟实现

template<class T>
class vector
{
public:// ......private:iterator _start; // 指向数据块的开始iterator _finish; // 指向有效数据的尾iterator _endOfStorage; // 指向存储容量的尾
};

4.1 实现迭代器

typedef T* iterator;
typedef const T* const_iterator;iterator begin()
{return _start;
}iterator end()
{return _finish;
}const_iterator cbegin() const
{return _start;
}const_iterator cend() const
{return _finish;
}

4.2 扩容

void reserve(size_t n)
{// 开辟新空间 把旧空间上的数据拷贝到新空间上 释放旧空间size_t oldsize = size();T* tmp = new T[n];// 如果原空间上有数据就要先拷贝到新空间上if (_start){memcpy(tmp, _start, sizeof(T) * size());delete[] _start;}_start = tmp;_endOfStorage = _start + n;//_finish = _start + size();// 有问题:此时的_start是新空间上的,不是原空间_finish = _start + oldsize;
}void resize(size_t n, const T& value = T())
{// 判断是否要扩容if (_finish == _endOfStorage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}
}

4.3 重载[]

T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos)const
{return _start[pos];
}

4.4 插入和删除

void push_back(const T& x)
{if (_finish == _endOfStorage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*(_finish) = x;++_finish;
}void pop_back()
{--_finish;
}iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _endOfStorage){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + len;}iterator tmp = _finish - 1;while (pos <= tmp){*(tmp + 1) = *tmp;--tmp;}*pos = x;++_finish;return pos;
}iterator erase(iterator pos)
{assert(pos <= _finish);assert(pos >= _start);iterator tmp = _finish - 1;while (pos < tmp){*pos = *(pos + 1);pos++;}--_finish;return pos;
}

4.5 构造函数和析构函数

vector()
{if (_finish == _endOfStorage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}
}vector(int n, const T& value = T())
{reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}
}template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}vector(const vector<T>& v)
{reserve(v.capacity());for (auto e : v){push_back(e);}
}vector<T>& operator= (vector<T> v)
{_start = v._start;_finish = v._finish;_endOfStorage = v._endOfStorage;
}~vector()
{if (_start){delete[] _start;_start = _finish = _endOfStorage = nullptr;}
}

5. 迭代器失效

迭代器失效常发生在扩容和越界时。

5.1 扩容后失效

void reserve(size_t n)
{// 开辟新空间 把旧空间上的数据拷贝到新空间上 释放旧空间size_t oldsize = size();T* tmp = new T[n];// 如果原空间上有数据就要先拷贝到新空间上if (_start){memcpy(tmp, _start, sizeof(T) * size());delete[] _start;}_start = tmp;_endOfStorage = _start + n;//_finish = _start + size();// 有问题:此时的_start是新空间上的,不是原空间_finish = _start + oldsize;
}

在实现插入接口时,如果不考虑记录oldsize就会出现迭代器失效问题。
在这里插入图片描述
在插入数据时,如果涉及扩容,也可能出现这种情况

iterator insert(iterator pos, const T& x)
{assert(pos <= _finish);assert(pos >= _start);if (_finish == _endOfStorage){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + len;}iterator tmp = _finish - 1;while (pos <= tmp){*(tmp + 1) = *tmp;--tmp;}*pos = x;++_finish;return pos;
}

在这里插入图片描述

5.2 越界失效

可能会发生在删除数据时
在这里插入图片描述

6. 深浅拷贝

if (_start)
{memcpy(tmp, _start, sizeof(T) * size());delete[] _start;
}

这段代码不用strcpy的原因是浅拷贝会引发二次析构。浅拷贝后的对象指向的空间和拷贝目标为同一块空间,第一次delete后这段空间消失,此时tmp相当于被析构了。但后续中,tmp还会再析构一次,造成二次析构导致程序崩溃。所以这里需要实现深拷贝。

http://www.xdnf.cn/news/8216.html

相关文章:

  • 系统性能分析基本概念(5) : 何时开始性能分析
  • 【语法】C++的map/set
  • 平安健康2025年一季度深耕医养,科技赋能见成效
  • Android Service与BroadcastReceiver深度解析:从零到一的实现与优化
  • python实现web请求
  • 力扣小题, 力扣113.路径总和II力扣.111二叉树的最小深度 力扣.221最大正方形力扣5.最长回文子串更加优秀的算法:中心扩展算法
  • 解压软件推荐:功能、优缺点及使用技巧
  • 【题解-洛谷】P11951 [科大国创杯初中组 2023] 数数
  • w~自动驾驶~合集13
  • 杰发科技AC7840——使用内部温度
  • IO多路复用
  • mysql 创建用户,创建数据库,授权
  • Spring Boot 内置工具类汇总与讲解
  • NumPy 2.x 完全指南【十八】数组元素的新增和删除
  • 数据结构与算法-算法复杂度
  • vue页面目录菜单有些属性是根据缓存读取的。如果缓存更新了。希望这个菜单也跟着更新。
  • 编程速递-RAD Studio 12.3 Athens五月补丁:May Patch Available
  • 制作一款打飞机游戏54:子弹编辑UI
  • 视频文件损坏怎么修复?4款专业视频修复工具推荐
  • JUC入门(六)
  • webpack性能优化
  • 【Pandas】pandas DataFrame round
  • 深入解读Qwen3技术报告(三):深入剖析Qwen3模型架构
  • 【网络篇】TCP协议的三次握手和四次挥手
  • 动手学深度学习12.5. 多GPU训练-笔记练习(PyTorch)
  • cider指标
  • 光谱相机在地质勘测中的应用
  • leetcode2261. 含最多 K 个可整除元素的子数组-medium
  • JAVA动态生成类
  • 在政务中使用仙盟创梦工具维护曲靖市麒麟公安分局————仙盟创梦IDE