【C++】vector的底层原理讲解及其实现

目录

一、认识vector底层结构
二、初始化vector的函数

  1. 构造函数
  2. 拷贝构造
  3. 赋值构造
  4. initializer_list构造
  5. 迭代器区间构造

三、迭代器
四、数据的访问
五、容量相关的函数
六、关于数据的增删查改操作


一、认识vector底层结构
STL库中实现vector其实是用三个指针来完成的,使用这三个指针(或称为迭代器)变量来管理其内存

在这里插入图片描述
其实vector和string的实现非常相似,都是利用了顺序表结构,在vector的实现上我们遵循底层用三个指针来完成,_statr,_finish,_end_fo_storage分别指向顺序表的头,顺序表存储数据的有效个数的位置,顺序表的结束

template<class T>
class vector
{
public:
	typedef T* iterator;
	typedef const T* const_iterator;
	
private:
	iterator _start;
	iterator _finish;
	iterator _endofstorage;
};

二、初始化vector的函数

1、构造函数
①最常用的无参默认构造
vector();

vector()
:_strat(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}

这个很简单,我就不多讲了,后面遇到难点我会详细说明,便于大家理解

②用n个val构造一个vector
explicit vector (size_type n, const value_type& val = value_type();
库里面用explicit修饰构造函数,是为了防止构造函数发生隐式类型转换

vector(size_t n, const T& val = T())
{
	_start = new T[n];
	_finish = _start;
	while (_finish!=_start+n)
	{
		*_finish = val;
		_finish++;
	}
	_endofstorage = _start + n;
}

2、拷贝构造
vector(const vector& v);
拷贝构造:用一个已经存在的对象来初始化另一个正在创建的对象

vector(const vector& v)
{
	_start = new T[v.size()];
	memcpy(_start, v._start, sizeof(T) * v.size());
	_finish = _start + v.size();
	_endofstorage = _finish;
}

3、赋值构造
赋值构造:两个已经存在的对象,一个赋值给另一个
vector& operator= (const vector& v);

void swap(vector& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_endofstorage, v._endofstorage);
}
//v1=v2;
vector& operator= (vector v)
{
	swap(v);
	return *this;
}

这里我利用库里的函数来实现swap,只交换vector的三个指针更高效,赋值构造的参数是传值传参,会调用拷贝构造,将v2拷贝一份给v,然后之间调用swap函数,将v和this交换,最后返回this即可

4、initializer_list构造
vector (initializer_list<T> il);
tips:这里的initializer_list实际是个类,C++底层将其封装了,里面也有begin,end,size

//vector<int> v={1,2,3,4,5};
vector(initializer_list<T> il)
{
	for (auto e : il)
	{
		push_back(e);
	}
}

5、迭代器区间构造
template <class InputIterator> vector(InputIterator first, InputIterator last);

// 类模板的成员函数可以是函数模板
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

注意:如果加了迭代器区间构造会造成一个问题,就是在调用时和vector(size_t n, const T& val = T())会出现冲突,底层给出的解决方案就是加一个重载vector(int n, const T& val = T())

三、迭代器
这里博主就只介绍最重要的迭代器部分
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//加了const修饰,const对象可以调用,非const对象也可以调用
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}
	private:
		iterator _start; //指向空间(顺序表)的开始
		iterator _finish;//指向有效数据个数的位置
		iterator _endofstorage;//指向空间的结束
};


四、数据的访问

由于vector底层是连续的物理空间,所以支持下标访问
T& operator[] (size_t n);
const T& operator[] (size_t n) const;

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

五、容量相关的函数

size(有效数据个数)和capacity(空间容量大小)

size_t size() const
{
	return _finish - _start;//指针减指针得到两个指针之间的数据个数
}
size_t capacity() const
{
	return _endofstorage - _start;
}

reserve
void reserve (size_t n);
易错点1
在这里插入图片描述

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t old_size = size();
		T* temp = new T[n];
		memcpy(temp, _start, sizeof(T) * size());
		delete[]_start;
		_start = temp;
		_finish = _start + old_size;
		_endofstorage = _start+n;
	}
}

改成现在这个样子确实是解决了上图中的问题,但是这个就是对的吗?让我们一起来看一下这个代码究竟对不对

易错点2
其实上面的代码大体逻辑是没有什么问题的,问题就出在这个memcpy上,要知道memcpy底层实现是按字节一个一个拷贝过去的,虽然我们的vector是深拷贝,但是memcpy是浅拷贝,这样写针对内置类型是🆗的,但是针对自定义类型会存在指向同一块空间的问题,两次析构等问题,话不多说,我们看图解

出错案例:
在这里插入图片描述
出错图解:
在这里插入图片描述
其实要改正这个问题有一个很简单的方式,采用赋值的方式,无论是内置类型还是自定义类型,在赋值时都会调用他的拷贝构造,这样就自动调用该类型的深拷贝了

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t old_size = size();
		T* temp = new T[n];
		//memcpy(temp, _start, sizeof(T) * size());
		for (size_t i = 0; i < old_size; i++)
		{
			temp[i] = _start[i];
		}
		delete[]_start;
		_start = temp;
		_finish = _start + old_size;
		_endofstorage = _start+n;
	}
}

resize
void resize (size_t n, const T& val=T());

void resize(size_t n, const T& val=T())
{
	if (n > capacity())
	{
		reserve(n);
		for (size_t i = size(); i < n; i++)
		{
			_start[i] = val;
		}
		_finish = _start + n;
	}
	else
	{
		_finish = _start + n;
	}
}

注意:reverseresize都不会缩容

empty
bool empty() const

bool empty()const
{
	return _finsh == _start;
}

六、关于数据的增删查改操作

push_back
void push_back (const T& val);

void push_back(const T& val)
{
	if (size() == capacity())
	{
		reserve(capacity() == 0 ? 4 : 2 * capacity());
	}
	*_finish = val;
	_finish++;
}

inserrt
void insert (iterator pos, const T& val);

void insert(iterator pos, const T& val)
{
	assert(pos>=_start);
	assert(pos <= _finish);
	size_t d = pos - _start;//先记下pos和_start的相对位置
	if (size() == capacity())
	{
		reserve(capacity() == 0 ? 4 : 2 * capacity());
		//如果扩容了,要更新pos
		pos = _start + d;
	}
	iterator end = _finish;
	while (pos < end)
	{
		*end = *(end - 1);
		end--;
	}
	*pos = val;
	_finish++;
}

erase
iterator erase (iterator pos);

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

clear
void clear();

void clear()
{
	_finish = _start;
}

vector章节我们就到这里啦,欢迎大家来学习指教下一篇list章节😘

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值