news 2026/4/20 2:50:14

vector模拟实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vector模拟实现

迭代器失效

我们先实现一个简单的vector逻辑

#pragma once #include<assert.h> #include<iostream> ​ namespace bit { template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; ​ iterator begin() { return _start; } ​ iterator end() { return _finish; } ​ const_iterator begin() const { return _start; } ​ const_iterator end() const { return _finish; } ​ vector() :_start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { } ​ ~vector() { if (_start) { delete[] _start; _start = _finish = _endofstorage = nullptr; } } ​ ​ ​ void reserve(size_t n) { if (n > capacity()) { size_t sz = size(); T* tmp = new T[n]; if (_start) { memcpy(tmp, _start, sizeof(T) * size()); delete[] _start; } _start = tmp; _finish = _start + sz; _endofstorage = _start + n; ​ } } ​ size_t capacity() const { return _endofstorage - _start; } ​ size_t size() const { return _finish - _start; } ​ T& operator[](size_t pos) { assert(pos < size()); return _start[pos]; } ​ const T& operator[](size_t pos) const { assert(pos < size()); return _start[pos]; } private: iterator _start; iterator _finish; iterator _endofstorage; }; }

迭代器失效1-扩容

在此基础上增加push_back和insert

​ void push_back(const T& x) { //if (_finish == _endofstorage) //{ // size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; // reserve(newcapacity); //} //*_finish = x; //++_finish; ​ insert(end(), x); } ​ void insert(iterator pos, const T& x) { assert(pos >= _start && pos <= _finish); ​ if (_finish == _endofstorage) { size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; reserve(newcapacity); } ​ iterator end = _finish - 1; ​ while (end >= pos) { *(end + 1) = *end; end--; } ​ *pos = x; ++_finish; }

这里push_back在调用insert时,就会出现一个典型问题:迭代器失效

当push_back 调用insert时,若需要扩容,则会进入reserve();

reserve()内部delete[]释放旧数组内存,_start指向新地址;

返回insert后,原来的pos位置(即旧finish的地址)已经指向被释放的内存,成为野指针,后续再对pos访问会导致未定义行为

解决方案:在可能引起扩容的操作之前,记录迭代器的相对位置(偏移量),扩容后重新计算新的迭代器

void insert(iterator pos, const T& x) { assert(pos >= _start && pos <= _finish); ​ if (_finish == _endofstorage) { size_t offset = pos - _start;//记录pos相对位置 size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; reserve(newcapacity); ​ pos = _start + offset;//更新pos } ​ iterator end = _finish - 1; ​ while (end >= pos) { *(end + 1) = *end; end--; } ​ *pos = x; ++_finish; }

此时解决了vector内部的迭代器失效问题,但是外部调用时,使用insert后迭代器仍可能失效(扩容)。

insert后不要再次使用形参迭代器。

C++标准库采用了iterator返回值方式来解决

iterator insert(iterator pos, const T& x) { ​ ... return pos; }

迭代器失效2-删除

接下来看一个删除的场景

void erase(iterator pos) { assert(pos >= _start && pos < _finish); ​ iterator it = pos + 1; while (it != _finish) { *(it - 1) = *it; it++; } --_finish; }

对erase进行调用

void test_2() { bit::vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(5); for (auto e : v1) { cout << e << " "; } cout << endl; ​ v1.erase(v1.begin() + 4); //erase后,迭代器失效,vs强制检查访问 auto it = v1.begin() + 4; v1.erase(it); cout << *it << endl;//未定义操作 it--;//未定义操作 cout << *it << endl;//未定义操作 ​ ​ }

erase后,it仍保留原来的内存地址,但这个地址已经不在容器有效范围内,it变成悬空迭代器;尽管--it后,仍能访问容器中的元素,但是在标准语义中,此时的it已经是失效迭代器,对it的任何操作都是未定义行为,尽管it可能和容器物理上紧邻。

vs中会强制检查erase后的迭代器,不允许对其再次访问

正确写法是返回迭代器

iterator erase(iterator pos) { assert(pos >= _start && pos < _finish); ​ iterator it = pos + 1; while (it != _finish) { *(it - 1) = *it; it++; } --_finish; return pos; }

深浅拷贝

拷贝构造与复制拷贝

//vector(const vector<T>& v) // :_start(nullptr) // , _finish(nullptr) // , _endofstorage(nullptr) //{ // _start = new T[v.capacity()]; // memcpy(_start, v._start, sizeof(T) * size()); // _finish = _start + v.size(); // _endofstorage = _start + v.size(); ​ //} ​ vector(const vector<T>& v) :_start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { reserve(v.capacity()); for (auto e : v) { push_back(e); } } ​ void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_endofstorage, v._endofstorage); } ​ vector<T>& operator=(vector<T> v) { swap(v); return *this; } ​

复制拷贝使用了copy-and-swap的典型实现

memcpy的拷贝问题

void reserve(size_t n) { if (n > capacity()) { size_t sz = size(); T* tmp = new T[n]; if (_start) { memcpy(tmp, _start, sizeof(T) * size());//memcpy delete[] _start; } _start = tmp; _finish = _start + sz; _endofstorage = _start + n; ​ } } ​ void test_4() { bit::vector<string> v; v.push_back("111111"); v.push_back("222222"); v.push_back("333333"); v.push_back("444444"); v.push_back("555555"); ​ for (auto& e : v) { cout << e << " "; } cout << endl; ​ }

如果扩容采用memcpy,此时新内存tmp与原指针指向同一块地址空间;

delete[]后旧内存被释放,源string被析构,此时tmp的string对象指针变成了悬空指针;

当再次对vector析构时,会导致对同一块内存二次释放,程序崩溃。

事实上:memcpy只是对源内存区域的数据原封不动复制到目标区域,逐字节拷贝,对于内置类型没有问题,但无法处理自定义类型

改进方案:

void reserve(size_t n) { if (n > capacity()) { size_t sz = size(); T* tmp = new T[n]; if (_start) { //memcpy(tmp, _start, sizeof(T) * size()); //memcpy逐字节拷贝,无法处理自定义类型 ​ for (size_t i = 0; i < size(); i++) { tmp[i] = _start[i]; } ​ delete[] _start; } _start = tmp; _finish = _start + sz; _endofstorage = _start + n; ​ } }

调用赋值运算符重载,实现对string的深拷贝

完整实现

#pragma once #include<assert.h> #include<iostream> ​ namespace bit { template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; ​ ​ iterator begin() { return _start; } ​ iterator end() { return _finish; } ​ const_iterator begin() const { return _start; } ​ const_iterator end() const { return _finish; } ​ vector(size_t n, const T& val = T()) :_start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { resize(n, val); } ​ vector() :_start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { } ​ ~vector() { if (_start) { delete[] _start; _start = _finish = _endofstorage = nullptr; } } ​ //vector(const vector<T>& v) // :_start(nullptr) // , _finish(nullptr) // , _endofstorage(nullptr) //{ // _start = new T[v.capacity()]; // memcpy(_start, v._start, sizeof(T) * size()); // _finish = _start + v.size(); // _endofstorage = _start + v.size(); ​ //} ​ vector(const vector<T>& v) :_start(nullptr) , _finish(nullptr) , _endofstorage(nullptr) { reserve(v.capacity()); for (auto e : v) { push_back(e); } } ​ void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_endofstorage, v._endofstorage); } ​ vector<T>& operator=(vector<T> v) { swap(v); return *this; } ​ void reserve(size_t n) { if (n > capacity()) { size_t sz = size(); T* tmp = new T[n]; if (_start) { //memcpy(tmp, _start, sizeof(T) * size());//memcpy逐字节拷贝,无法处理自定义类型 ​ for (size_t i = 0; i < size(); i++) { tmp[i] = _start[i]; } ​ delete[] _start; } _start = tmp; _finish = _start + sz; _endofstorage = _start + n; ​ } } ​ size_t capacity() const { return _endofstorage - _start; } ​ size_t size() const { return _finish - _start; } ​ T& operator[](size_t pos) { assert(pos < size()); return _start[pos]; } ​ const T& operator[](size_t pos) const { assert(pos < size()); return _start[pos]; } ​ void pop_back() { erase(--end()); } ​ void resize(size_t n, const T& val = T()) { if (n < size()) { _finish = _start + n; } else { reserve(n); while (_finish != _start + n) { *_finish = val; ++_finish; } } } ​ void push_back(const T& x) { //if (_finish == _endofstorage) //{ // size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; // reserve(newcapacity); //} //*_finish = x; //++_finish; ​ auto it = insert(end(), x); } ​ iterator insert(iterator pos, const T& x) { assert(pos >= _start && pos <= _finish); ​ if (_finish == _endofstorage) { size_t offset = pos - _start; size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; reserve(newcapacity); ​ pos = _start + offset; } ​ iterator end = _finish - 1; ​ while (end >= pos) { *(end + 1) = *end; end--; } ​ *pos = x; ++_finish; return pos; } ​ iterator erase(iterator pos) { assert(pos >= _start && pos < _finish); ​ iterator it = pos + 1; while (it != _finish) { *(it - 1) = *it; it++; } --_finish; return pos; } ​ private: iterator _start; iterator _finish; iterator _endofstorage; }; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/20 2:47:33

零停机迁移:如何将服务器成本从 $1432 降至 $233

零停机迁移&#xff1a;如何将服务器成本从 $1432 降至 $233 在云计算大行其道的今天&#xff0c;"便利性"往往伴随着昂贵的溢价。对于初创公司和个人开发者而言&#xff0c;当业务规模趋于稳定&#xff0c;基础设施成本便成了不可忽视的利润黑洞。本文将详细复盘一次…

作者头像 李华
网站建设 2026/4/20 2:46:22

hot100内容(1、哈希--矩阵)

文章目录 哈希 1、两数之和 2、字母异位词分组 3、最长连续序列 双指针 1、移动零 2、盛最多水的容器 3、三数之和 4、接雨水 滑动窗口 1、无重复字符的最长字串 2、找到字符串中所有字母异位词 子串 1、和为K的子数组 2、滑动窗口的最大值 3、最小覆盖子串 普通数组 1、最大子…

作者头像 李华
网站建设 2026/4/20 2:45:32

告别截图!用mutool draw命令把PDF批量转成高清PNG图片(附Python脚本)

高效PDF转PNG全攻略&#xff1a;用mutool实现批量自动化处理 每次需要从PDF中提取页面制作演示文稿或分享内容时&#xff0c;手动截图不仅效率低下&#xff0c;画质也难以保证。作为经常处理技术文档的内容创作者&#xff0c;我发现mutool这个命令行工具能完美解决这个问题——…

作者头像 李华
网站建设 2026/4/20 2:45:29

SQL处理分组聚合中的数据一致性_使用事务保证

分组聚合后更新必须显式开启事务并使用高隔离级别&#xff0c;避免竞态条件&#xff1b;需清洗分组字段防隐式转换&#xff1b;禁用UPDATE中嵌套GROUP BY子查询&#xff0c;改用CTE预计算&#xff1b;避免事务内混合DDL&#xff1b;确保锁覆盖所有相关行。分组聚合前必须显式开…

作者头像 李华
网站建设 2026/4/20 2:34:45

展讯平台Android系统定制避坑指南:从预装应用到开机动画的实战修改

展讯平台Android系统深度定制实战&#xff1a;关键模块修改与性能优化 在移动设备开发领域&#xff0c;展讯(SPRD)平台因其高性价比和灵活性&#xff0c;成为众多厂商的选择。但不同于主流芯片平台&#xff0c;展讯在系统定制方面存在诸多独特机制和"坑点"。本文将深…

作者头像 李华