vector简单实现


前言

    std::vector 是 C++ 标准库中的一个动态数组容器类,std::vector 可以动态调整其大小,因此在需要频繁尾插尾删的场景下非常有用, 这次我们来学习并简单实现一下
![](https://i-blog.csdnimg.cn/direct/98392dc8544843c19eeea8491796d53b.png

vector接口介绍

构造函数

    std::vector 提供了多种构造函数, 可以适用很多场景, 以下是常见的构造

vector();//默认构造
explicit vector (size_type n, const allocator_type& alloc = allocator_type());//提前开n个空间
vector (size_type n, const value_type& val,const allocator_type& alloc = allocator_type());//提前开n个空间并初始化为val
template <class InputIterator>
vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());//迭代器范围构造
vector (const vector& x);//拷贝构造
vector (vector&& x);//右值引用拷贝构造
vector (initializer_list<value_type> il,const allocator_type& alloc = allocator_type());//列表初始化

    平时我们最常用的就是默认构造和提前开空间的构造

迭代器

    vector也设计了一套迭代器, 用于遍历和操作vector容器中的元素的工具。它们类似于指针,可以访问vector中的元素。通过迭代器,可以在vector中进行遍历、插入、删除等操作, 以下是常用的迭代器方法

//返回普通或常量迭代器,指向 vector 的第一个元素的位置
iterator begin() noexcept;
const_iterator begin() const noexcept;
//返回普通或常量迭代器,指向 vector 的最后一个元素的位置
iterator end() noexcept;
const_iterator end() const noexcept;
//返回反向迭代器,指向 vector 第一个元素的反向迭代器。
reverse_iterator rbegin() noexcept;
const_reverse_iterator rbegin() const noexcept;
//返回反向迭代器,指向 vector 最后一个元素的反向迭代器。
reverse_iterator rend() noexcept;
const_reverse_iterator rend() const noexcept;`

示例

#include <iostream>
#include <vector>
int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};

    // 使用普通迭代器遍历
    for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << " ";  // 使用 * 解引用迭代器以访问元素
    }
    std::cout << std::endl;

    return 0;
}

要是觉得这个类型太长, 直接auto it=v.begin()即可

容量操作

size_type size() const noexcept;//获取当前元素个数
void resize (size_type n);//调整 vector 的大小,使其包含 n 个元素
void resize (size_type n, const value_type& val);//调整 vector 的大小,使其包含 n 个元素, 并初始化为wal
size_type capacity() const noexcept;//获取当前空间大小
bool empty() const noexcept;//判断是否为空
void reserve (size_type n);//调整空间, 如果n比当前空间大, 就调整空间到n, 如果n小, 就什么也不做

注意

  • 在resize中如果 n 小于当前的 size(),则 vector 将被缩小,多余的元素将被移除
  • reserve 不改变 size(),只是为未来可能的扩展预留空间。它不会缩减 capacity(),因此也不会释放内存

元素访问操作

    除了使用迭代器访问元素, 也可以通过vector提供的接口访问指定位置的元素

reference operator[] (size_type n);
const_reference operator[] (size_type n) const;

    vector也重载了[], 使得能够和使用数组一样访问它

reference at (size_type n);
const_reference at (size_type n) const;

    如果不想使用[], 也可以使用at获取一个位置元素的引用

reference front();//返回第一个元素的引用
const_reference front() const;//返回第一个元素的const引用
reference back();//返回最后一个元素的引用
const_reference back() const;//返回最后一个元素的cosnt的引用

常用操作

    下面来介绍vector的常见使用操作

void push_back (const value_type& val);
void push_back (value_type&& val);
void pop_back();

    经典尾插尾删

//在 position 位置插入一个值为 val 的元素。返回指向新插入元素的迭代器。
iterator insert (const_iterator position, const value_type& val);
//在 position 位置插入 n 个值为 val 的元素。返回指向第一个新插入元素的迭代器
iterator insert (const_iterator position, size_type n, const value_type& val);
//在 position 位置插入范围 [first, last) 中的元素。返回指向第一个新插入元素的迭代器
template <class InputIterator>
iterator insert (const_iterator position, InputIterator first, InputIterator last);
//在 position 位置插入一个右值引用 val,返回指向新插入元素的迭代器。
iterator insert (const_iterator position, value_type&& val);
//在 position 位置插入初始化列表中的所有元素。返回指向第一个新插入元素的迭代器。
iterator insert (const_iterator position, initializer_list<value_type> il);

    经典插入, 这些函数提供了灵活的方式来在 vector 中插入元素

template <class... Args>
iterator emplace (const_iterator position, Args&&... args);//position位置插入, 使用后面的模板可变参数构造对象
template <class... Args>
void emplace_back (Args&&... args);//尾插, 使用模板可变参数构造对象

    emplace 和 emplace_back 允许你直接在容器中原地构造对象,避免了额外的复制或移动操作。

iterator erase (const_iterator position);//删除position位置元素,返回指向被删除元素后一个位置的迭代器
iterator erase (const_iterator first, const_iterator last);//删除一个范围的元素, 返回last位置元素的迭代器

    这里会出现迭代器失效的问题, vector是一个顺序表, 删除操作代表着会将其后面所有的元素都移动一位, 所以如果使用一个已经被删除的元素的位置的迭代器, 就会出现迭代器失效的问题, 所以erase才会返回一个有效的迭代器
示例

#include <iostream>
#include <vector>
int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    auto it=v.begin();
    while(it!=v.end())
    {
        if(*it==1)  it=erase(it);//删除1后, it失效, 使用erase的返回值重新赋值
        else it++;
    }
    return 0;
}

vector的简单实现

    我们已经熟悉了vector的常见操作, 这就是封装了的顺序表, 现在我们来简单实现一下
完整代码

框架

    只实现了常用的接口, 这里只介绍一些重点方法实现, 其他的可以去我的GitHub仓库看源码

template<class T>
class vector
{
public:
    //迭代器部分
	typedef T* iterator;
	typedef const T* const_iterator;
	iterator begin();
	iterator end();
	const_iterator begin() const;
	const_iterator end() const;
    //构造函数
	vector();
	vector(const vector<T>& v);
	vector(vector<T> && v);
	vector(std::initializer_list<T> li);
	template<class ITERATOR>;
	vector(ITERATOR begin, ITERATOR end);
    //析构
	~vector();
    //插入
	void push_back(const T& val);
	void push_back(T&& val);
	void pop_back();
	void insert(iterator pos,const T& val);
	void insert(iterator pos,T&& val);
	template<class... Args>
	void emplace_back(Args... args);
    //删除
	iterator erase(iterator pos);
	void swap(vector<T>& v);
	T& front();
	const T& front() const;
	T& back();
	const T& back() const;
    //空间
	size_t capacity() const;
	size_t size() const;
	void reserve(const size_t n);
	void resize(const size_t n, T val = T());
    //操作符重载
	T& operator[](size_t pos);
	const T& operator[](size_t pos) const;
	vector<T>& operator=(const vector<T>& v);
	vector<T>& operator=(vector<T>&& v);
	vector<T>& operator=(std::initializer_list<T> li);
private:
	iterator _start=nullptr;//指向开辟的空间的头
	iterator _finish=nullptr;//指向使用的空间的元素末尾
	iterator _end=nullptr;//指向开辟的空间的尾位置的下一个
};

构造/析构

//默认构造
vector()
{}
//拷贝构造
vector(const vector<T>& v)
{
    //开和要拷贝的vector一样的空间
	reserve(v.capacity());
	for (auto& e : v)
	{
		push_back(e);
	}
}
//右值引用版拷贝构造
vector(vector<T> && v)
{
	swap(v);
}
//列表构造
vector(std::initializer_list<T> li)
{
	for (auto& i : li)
	{
		push_back(i);
	}
}
//范围迭代器构造
template<class ITERATOR>
vector(ITERATOR begin, ITERATOR end)
{
	while (begin != end)
	{
		push_back(*begin);
		begin++;
	}
}
//析构
~vector()
{
	delete[] _start;
	_start = _finish = _end = nullptr;
}

注意

  • 如果插入了数据,一定会调用new[],析构也要相应匹配

迭代器

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;
}

    这里直接使用指针就能实现迭代器的功能, 不必再过多封装了

操作符重载

template<class T>
T& vector<T>::operator[](size_t pos)
{
	assert(pos < size());
	return *(_start + pos);
}
template<class T>
const T& vector<T>::operator[](size_t pos)const
{
	assert(pos < size());
	return *(_start + pos);
}
template<class T>
vector<T>& vector<T>::operator=(const vector<T>& v)
{
	for (auto& i : v)
	{
		push_back(i);
	}
	return *this;
}
template<class T>
vector<T>& vector<T>::operator=(std::initializer_list<T> li)
{
	vector<T> v(li);
	swap(v);
	return *this;
}

注意

  • =操作符要支持连接=
  • 检查越界情况

常用操作

容量操作

void vector<T>::reserve(const size_t n)
{
	if (n > capacity())
	{
		size_t oldn = size();
		T* temp = new T[n];
		if(_start!=nullptr)
		{
			size_t end = size();
			while (end > 0)
			{
				temp[end-1] = _start[end-1];
				end--;
			}
			delete[] _start;
		}
		_start = temp;
		_finish = _start + oldn;
		_end = _start + n;
	}
}
template<class T>
void vector<T>::resize(const size_t n, T val)
{
	if (n > size())
	{
		reserve(n);
		for (size_t i = size(); i < n; i++)
		{
			_start[i] = val;
			_finish++;
		}
	}
	else
	{
		_finish = _start+ n;
	}
}

注意

  • 扩容后释放旧空间
  • _end表示最后一个位置的下一个

插入

template<class T>
void vector<T>::push_back(const T& val)
{
	if (_finish == _end)
	{
        //第一次开4个空间, 以后是2倍扩容
		size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
		reserve(newcapacity);
	}
	*_finish = val;
	++_finish;
}
//右值引用版尾插
template<class T>
void vector<T>::push_back(T&& val)
{
	if (_finish == _end)
	{
		size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
		reserve(newcapacity);
	}
	*_finish = std::forward<T>(val);
	++_finish;
}
template<class T>
void vector<T>::pop_back()
{
	assert(size());
	--_finish;
}
template<class T>
void vector<T>::insert(iterator pos, T && val)
{
	assert(_start <= pos);
	assert(_finish >= pos);
	if (_finish == _end)
	{
		size_t n = pos - _start;
		size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
		reserve(newcapacity);
		pos = _start + n;
	}
	_finish++;
	iterator end = _finish - 1;
	while (end != pos)
	{
		*end = *(end - 1);
		end--;
	}
	*pos = std::move(val);
}
template<class T>
void vector<T>::insert(iterator pos,const T& val)
{
	assert(_start <= pos);
	assert(_finish>= pos);
	if (_finish == _end)
	{
		size_t n = pos - _start;
		size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
		reserve(newcapacity);
		pos = _start + n;
	}
	_finish++;
	iterator end = _finish-1;
	while (end != pos)
	{
		*end = *(end - 1);
		end--;
	}
	*pos = val;
}

注意

  • 满了需要扩容, 我这里选择的第一次开辟4个空间, 接下面都是2倍扩容
  • insert也要提前计算空间大小, 然后移动位置插入
  • 右值引用的插入要完美转发

删除

iterator erase(iterator pos)
{
	assert(size());
	assert(_start <= pos);
	assert(_finish >= pos);
	iterator end = pos;
	int n = pos - _start;
	while (end < _finish)
	{
		*end = *(end + 1);
		end++;
	}
	--_finish;
	return _start + n;
}

注意

  • 检查元素个数, 检查pos位置是否越界
  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值