目录
[vector 与 string 区别](#vector 与 string 区别)
[vector 的成员变量](#vector 的成员变量)
[构造 析构 Construct Destructor:](#构造 析构 Construct Destructor:)
[迭代器 Iterators:](#迭代器 Iterators:)
[容量 Capacity:](#容量 Capacity:)
[元素访问 Element access:](#元素访问 Element access:)
[修改器 Modifiers:](#修改器 Modifiers:)
[比较 relational operators:](#比较 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)
cpp
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:
cpp
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
cpp
vector(){}
2. 将 n 个 val 作为初始化
cpp
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. 将不同类型的变量,对应位置的迭代器位置区间的变量
cpp
/*
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.拷贝构造
cpp
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();
}
析构
cpp
~vector()
{
if (_start != nullptr)
{
for (iterator it = _start; it != _finish; it++)
{
it->~value_type();
}
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
}
析构得注意一下:vector 是模板类,可以对 string 等进行类类型的使用,而在对应类型析构后析构不能直接析构,要先析构成员每一个变量,在析构内存进行空间释放

运算符 = 重载(现代写法)
cpp
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:
cpp
//迭代器
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:
cpp
//基本成员函数
//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修改
cpp
//扩容
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:
cpp
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:
cpp
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
cpp
//插入数值
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 插入
cpp
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 的位置。
cpp
template <class InputIterator>
void insert(iterator position, InputIterator first, InputIterator last)
cpp
void insert(iterator position, size_t n, const value_type& val)
涉及到一些类模板的相关知识如果这么写会导致,这两个代码识别出现问题,会导致编译器错乱的情况。编译器不会调用上面第二行的函数代码,而是调用模板函数的代码。

在标准库中 vector 中简单了解即可,大概意思就是判断传输过来的是否是类对应的迭代器,不是就调用其他的符合的函数,是就调用该模板函数。就是限制只接收迭代器。
cpp
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 是类模板,清除要考虑对对象进行析构。
cpp
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:
cpp
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++ 知识库 - 码云 - 开源中国
感谢观看!
悠仁さん
