目录
vector 与 string 区别
vector 的成员变量
构造 析构 Construct Destructor:
迭代器 Iterators:
容量 Capacity:
元素访问 Element access:
修改器 Modifiers:
比较 relational operators:
vector 与 string 区别
vector 与 string 比较相似,但就不像 string 需要考虑字符串相关的问题,vector 要真作区分的话,string 更像char* 而 vector 类似其他整形浮点型的数组,但是这么讲不完全准确,vector是一个模板类,每次需要调用 vector 都需要一个<int> 等等类型甚至可以用 vector<string> 。
- 存储内容
string:专门存字符(char),代表字符串,末尾自带'\0';vector<T>:泛型容器,T 可以是任意类型(int、对象等),不限于字符,没有末尾 '\0'。- 模板属性
vector是类模板;string是实例化好的具体类(本质vector<char>的特化版本,但不等同)。- 功能侧重点
string:面向字符串操作,支持+=、c_str()、查找子串、字符串比较等字符串专用接口。vector:通用动态数组,侧重增删元素,没有字符串相关函数。- size 含义string 的 size 是有效字符个数,不计末尾
\0; vector 的 size 是有效元素个数。- 使用场景string:处理文本、字符串; vector:存储任意类型一组数据。
vector 的成员变量
vector 的成员变量由三个模板类型指针构成,分别指向数组三个位置头、(内容)尾 和(容量)尾 (注意应该是模板类的原因,无法做到定义分离的情况,都写在.h)
namespace Youren { template <class T>//class Alloc = allocator<T> 这个与内存池相关目前实现比较简单的 vector 类即可 class vector { public: //必须知道的事项 // type 的重命名 以及 迭代器 typedef T value_type; typedef const T value_type; typedef T* iterator; typedef const T* const_iterator; // 引用 typedef value_type& reference; typedef const value_type& const_reference; //虽然 size_type 也要重命名,但我还是比较用 size_t 习惯 private: iterator _start = nullptr; // 内容开始位置 iterator _finish = nullptr; // 内容末尾 iterator _end_of_storage = nullptr; // 容量末尾 }; }构造 析构 Construct Destructor:
vector();//无传参 初始化 vector(size_t n, const value_type& val = value_type()); // 将 n 个 val 匿名对象 无传参默认为 0 template <class InputIterator> vector(InputIterator first, InputIterator last); //适配各种迭代器 list string 等 将其初始化为vector<InputIterator> vector(const vector& x); //复制拷贝构造 ~vector()//析构 reference operator=(vector v)//运算符重载 =构造
1. vector()
无实数传参无需分配空间统一三个指针指向nullptr
vector(){}2. 将 n 个 val 作为初始化
vector(size_t n, const value_type& val = value_type()) // 目标 n 个 val ,val 是匿名对象 无传参默认为0 { if (n == 0) { return; } //步骤开空间 写入内容 标记好位置 _start = new value_type[n]; _finish = _start + n; _end_of_storage = _start + n; for (size_t i = 0; i < n; i++) { _start[i] = val; } }3. 将不同类型的变量,对应位置的迭代器位置区间的变量
/* false template <class InputIterator> vector(InputIterator first, InputIterator last) { size_t size_input = last - first; _start = new InputIterator[size_input]; _finish = _start + size_input; _end_of_storage = _start + size_input; for (size_t i = 0; i < size_input; i++) { _start[i] = first[i]; } } */ template <class InputIterator> vector(InputIterator first, InputIterator last) { for (InputIterator it = first; it != last; it++) { push_back(*it); } }注意第一种写法是错误的只能适配部分的类型,如果出现一些变化会导致类型野指针和计算容量出现问题等情况,主要问题是第一种类型只对连续开辟空间适配,对于非连续空间会导致计算空间出现问题,例如:list 就是典型的非连续空间的类类型。而却第一种写法可读性比较差,第二种的写法比较简洁,但要注意因为之中写法还是有缺点,就是无法做到如图下操作。(class Alloc = allocator<T> 与相关还无法实现)
4.拷贝构造
vector(const vector& x) { //开辟空间 _start = new value_type[x.capacity()]; //cpy for (size_t i = 0; i < x.size(); i++) { _start[i] = x[i]; } //标记结束_finish _finish = _start + x.size(); // 标记 _end_of_storage _end_of_storage = _start + x.capacity(); }析构
~vector() { if (_start != nullptr) { for (iterator it = _start; it != _finish; it++) { it->~value_type(); } delete[] _start; _start = _finish = _end_of_storage = nullptr; } }析构得注意一下:vector 是模板类,可以对 string 等进行类类型的使用,而在对应类型析构后析构不能直接析构,要先析构成员每一个变量,在析构内存进行空间释放
运算符 = 重载(现代写法)
void swap(vector& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_end_of_storage, v._end_of_storage); } reference operator=(vector v) { swap(v); return *this; }迭代器 Iterators:
//迭代器 iterator end() { return _finish; } iterator begin() { return _start; } const_iterator end() const { return _finish; } const_iterator begin() const { return _start; }功能说明:
- begin():返回指向容器第一个有效元素的迭代器,即内容头指针
_start。通过它可以从头开始遍历容器中的元素。- end():返回指向容器末尾之后位置的迭代器,即内容尾指针
_finish。它不指向任何有效元素,通常作为遍历的结束标志,配合begin()使用。- const 版本:
const_iterator begin() const和const_iterator end() const用于只读访问,在 const 对象上调用时返回const_iterator,只能读取元素,不能通过它修改元素内容。
容量 Capacity:
//基本成员函数 //size capacity // 注意谁前谁后 size_t size() const { return _finish - _start; } size_t capacity() const { return _end_of_storage - _start; } //判断是否为空 bool empty() const { return _start == _finish; }功能说明:
- size():返回当前有效元素个数,通过内容尾指针
_finish减去内容头指针_start得到,即_finish - _start。- capacity():返回当前容量,即最多能容纳的元素个数,通过容量尾指针
_end_of_storage减去内容头指针_start得到,即_end_of_storage - _start。- empty():判断容器是否为空,当内容头指针
_start与内容尾指针_finish相等时返回 true,表示没有有效元素。
reserve resize 扩容 size修改
//扩容 void reserve(size_t n = 4) { if (n <= capacity()) { return; } else { if (capacity() != 0) { n = n >= 2 * capacity() ? n : 2 * capacity(); } //注意扩容时 vector 具有特殊性会 3 迭代器指针位置出现头 delete[] 同时删除时会导致 其他两个地址变成野指针 //申请新的地址空间 iterator new_start = new value_type[n]; //记录就空间有效位置所占位置 size_t old_size = size(); //cpy //尽量不要使用memcpy for (size_t i = 0; i < size(); i++) { new_start[i] = _start[i]; } //删除旧空间 for (size_t i = 0; i < size(); i++) { _start[i].~value_type(); } delete[] _start; //换上新地址 并且依次配对地址 _start = new_start; _finish = _start + old_size; _end_of_storage = _start + n; } } // 匿名对象 匿名对象不传参默认该类型相关 0 的数值; void resize(size_t n, value_type val = value_type()) { if (n < size()) { iterator it = _finish - 1; for (; it != _start + n; it--) { it->~value_type(); } _finish = it; } else if (n > size()) { reserve(n); for (size_t i = size(); i < n; i++) { _start[i] = val; } _finish += (n - size()); } }上面写的不一定与源代码的扩容一致,但意思和算法,能够实现目的即可,reserve 和resize的目的与 string 中的别无二致。
注意点:发生扩容的时候会导致原先的记录的迭代器失效(就是原先的指针变成野指针),发生扩容时也需要更新迭代器,可以预先储存好相对位置,确保位置。
元素访问 Element access:
reference operator[](const size_t i) { assert(i < size()); return _start[i]; } const_reference operator[](const size_t i) const { assert(i < size()); return _start[i]; }功能说明:
- operator[]:通过下标
i直接访问容器中第i个元素,返回该元素的引用reference,可用于读取或修改对应位置的元素。- const 版本:
const_reference operator[](const size_t i) const用于只读访问,在 const 对象上调用时返回const_reference,只能读取元素,不能通过它修改元素内容。- 越界检查:两个版本都通过
assert(i < size())进行越界检查,当下标i超出当前有效元素个数时触发断言,帮助在调试阶段及时发现越界访问问题。
flont 和 back 都与 begin 和 end 一致。at 与 operator[] 用法相同;
修改器 Modifiers:
void push_back(const value_type& val);// 尾插入 1 个 val void pop_back();// 删除尾内容 void clear();// 全部内容删除 iterator insert(iterator position, const value_type& val);// 指定位置插入 1 个 val void insert(iterator position, size_t n, const value_type& val);// 指点位置插入 n 个 val iterator erase(iterator position);//删除指点位置 iterator erase(iterator first, iterator last);//删除对应区间实现原理原理都与string 都是相似的,但是 vector 凡事涉及到删除相关的内容都需要调用析构注意这一点就可以。
push_back , pop_back 和 clear
//插入数值 void push_back(const value_type& val) { //扩容 reserve(size() + 1); new(_finish)value_type(val); _finish++; } // void pop_back() { _finish--; _finish->~value_type(); } void clear() { for (iterator it = _start; it != _finish; it++) { it->~value_type(); } _finish = _start; }功能说明:
- push_back():在容器末尾插入一个元素
val。先调用reserve(size() + 1)确保容量足够,再通过new(_finish)value_type(val)在_finish位置原地构造新元素,最后_finish++更新内容尾指针。- pop_back():删除容器末尾的一个元素。先将
_finish前移一位,再调用_finish->~value_type()析构该位置的元素,避免内存泄漏。- clear():清空容器中所有元素。遍历
_start到_finish之间的每个元素并调用析构函数,最后将_finish重置为_start,使容器变为空状态。
insert 插入
iterator insert(iterator position, const value_type& val) //插入在pos 的位置 pos 一个 val 原本的内容往后调整 { assert(_start <= position && _finish >= position); //注意扩容可能带来迭代器失效 possiton 可能直接废了 size_t pos = position - _start;//记录相对位置 reserve(size() + 1); position = _start + pos; //移位 for (size_t i = size(); i != pos; i--) { _start[i] = _start[i - 1]; } //插入 _finish++; new(position)value_type(val); return position; } void insert(iterator position, size_t n, const value_type& val) { if (n == 0) { return; } assert(_start <= position && _finish >= position); size_t pos = position - _start; reserve(size() + n); position = _start + pos; for (size_t i = size() + n - 1; i >= pos + n; i--) { _start[i] = _start[i - n]; } for (size_t i = 0; i < n; i++) { new(position + i)value_type(val); } _finish += n; }注意:在扩容会导致迭代器失效,要时刻注意,扩容导致原先的_start 发生改变,position 的迭代器失效变成野指针,要确保迭代器时刻进行修改,需要保留一个相对位置 pos 的位置。
template <class InputIterator> void insert(iterator position, InputIterator first, InputIterator last)void insert(iterator position, size_t n, const value_type& val)涉及到一些类模板的相关知识如果这么写会导致,这两个代码识别出现问题,会导致编译器错乱的情况。编译器不会调用上面第二行的函数代码,而是调用模板函数的代码。
在标准库中 vector 中简单了解即可,大概意思就是判断传输过来的是否是类对应的迭代器,不是就调用其他的符合的函数,是就调用该模板函数。就是限制只接收迭代器。
template <class _Iter, enable_if_t<_Is_iterator_v<_Iter>, int> = 0> _CONSTEXPR20 iterator insert(const_iterator _Where, _Iter _First, _Iter _Last) { const pointer _Whereptr = _Where._Ptr; auto& _My_data = _Mypair._Myval2; const pointer _Oldfirst = _My_data._Myfirst; #if _ITERATOR_DEBUG_LEVEL == 2 _STL_VERIFY( _Where._Getcont() == _STD addressof(_My_data) && _Whereptr >= _Oldfirst && _My_data._Mylast >= _Whereptr, "vector insert iterator outside range"); #endif // _ITERATOR_DEBUG_LEVEL == 2 _STD _Adl_verify_range(_First, _Last); auto _UFirst = _STD _Get_unwrapped(_First); auto _ULast = _STD _Get_unwrapped(_Last); const auto _Whereoff = static_cast<size_type>(_Whereptr - _Oldfirst); if constexpr (_Is_cpp17_fwd_iter_v<_Iter>) { const auto _Length = static_cast<size_t>(_STD distance(_UFirst, _ULast)); const auto _Count = _STD _Convert_size<size_type>(_Length); _Insert_counted_range(_Where, _UFirst, _Count); #if _HAS_CXX20 } else if constexpr (forward_iterator<_Iter>) { const auto _Length = _STD _To_unsigned_like(_RANGES distance(_UFirst, _ULast)); const auto _Count = _Convert_size<size_type>(_Length); _Insert_counted_range(_Where, _UFirst, _Count); #endif // _HAS_CXX20 } else { _Insert_uncounted_range(_Where, _UFirst, _ULast); } return _Make_iterator_offset(_Whereoff); }erase 清除区域内容
与 insert 相反先清理,再移位。还是注意那个点,vector 是类模板,清除要考虑对对象进行析构。
iterator erase(iterator position)//清除该位置内容 { assert(_start <= position && _finish >= position); iterator it = position; it->~value_type(); for (; it + 1 != _finish; it++) { *it = *(it + 1); } _finish--; return position; } iterator erase(iterator first, iterator last) { assert(_start <= first && first <= last && last <= _finish); size_t pop = last - first; for (iterator it = first; it != last; it++) { it->~value_type(); } for (iterator it = first; (it + pop) != _finish; it++) { *it = *(it + pop); } _finish -= pop; return first; }比较 relational operators:
bool operator==(const vector& rhs) const { bool infer = false; if (size() == rhs.size()) { infer = true; for (size_t i = 0; i < size() && infer; i++) { if (_start[i] != rhs[i]) { infer = false; } } } return infer; } bool operator> (const vector& rhs) const { size_t min_size = size() < rhs.size() ? size():rhs.size(); for (size_t i = 0; i < min_size ; ++i) { if (_start[i] > rhs[i]) { return true; } else if (_start[i] < rhs[i]) { return false; } } return size() > rhs.size(); } bool operator< (const vector& rhs) const { return rhs > *this; } bool operator!=(const vector& rhs) const { return !(*this == rhs); } bool operator>=(const vector& rhs) const { return !(*this < rhs); } bool operator<=(const vector& rhs) const { return !(*this > rhs); }26_9_22 vector · 浪子·悠仁/C++ 知识库 - 码云 - 开源中国
感谢观看!
悠仁さん