C++vector类的常见函数以及其模拟实现

C++vector类的常见函数以及其模拟实现


前言

C++中引入了stl标准模板库,其中就包括了vector。
vector的核心就是数据结构中的顺序表。不过这个顺序表是动态开辟的,可随着用户的不断插入而增大容量。当然,每次增大容量都必须去耗费时间去copy数据,所以vector中通常会预留一些空间来提高效率。接下来我们就来介绍一下vector的常见函数以及它的模拟实现。


一、vector的内部成员变量有什么?

首先定义了一个模板T,这样可以在vector中插入不同的数据类型,当然也包括自定义数据类型。
然后将T*重命名为iterator(增加可读性)。
_start 指向vector的第一个元素的位置。
_finish 指向vector最后一个元素的下一个位置。
_end_of_storage 指向_start+容量的位置。(表示容量)

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

二、vector的构造\析构函数以及=的重载

1.默认构造

vector()
	:_start(nullptr),
	_finish(nullptr),
	_end_of_storage(nullptr)
{}

2.迭代器构造

这里定义了一个迭代器模板InputIterator,使得不管传入什么样的迭代器都可以进行构造。

template<class InputIterator>
vector(InputIterator start, InputIterator end)
	:_start(nullptr),
	_finish(nullptr),
	_end_of_storage(nullptr)
{
	while (start != end)
	{
		push_back(*start); // push_back即在末尾插入元素
		start++;
	}
}

3.传参构造

之所以又定义了一个int类型的重载,是因为当传入参数为int&&模板类型T=int时,这时编译器会默认去调用迭代器构造,从而出错。

vector(size_t n, const T& value = T())
	:_start(nullptr),
	_finish(nullptr),
	_end_of_storage(nullptr)
{
	reserve(n);
	for (size_t i = 0; i < n; i++)
	{
		_start[i] = value;
	}
	_finish = _start + n;
	_end_of_storage = _finish;
}

vector(int n, const T& value = T())
	:_start(nullptr),
	_finish(nullptr),
	_end_of_storage(nullptr)
{
	reserve(n);
	for (int i = 0; i < n; i++)
	{
		_start[i] = value;
	}
	_finish = _start + n;
	_end_of_storage = _finish;
}

4.拷贝构造以及重载

这里的cbegin,cend会传回const_iterator
swap 会将两个vector的成员依次交换(后面我们会提到)

vector(const vector<T>& vv)
	:_start(nullptr),
	_finish(nullptr),
	_end_of_storage(nullptr)
{
	reserve(vv.capacity());
	const_iterator it = vv.cbegin();
	iterator me = begin();
	while (it != vv.cend())
	{
		*(me++) = *(it++);
	}
	_finish = _start + vv.size();
	_end_of_storage = _finish;
}

vector<T> operator=(vector<T>& v)
{
	vector<T> temp(v);
	swap(temp);
	return *this;
}

5.析构函数

~vector()
{
	delete[] _start;
	_start = nullptr;
	_end_of_storage = nullptr;
	_finish = nullptr;
}

三.常见函数

1.reserve函数

传入一个变量n,若n大于其容量,则去扩容。
扩容后将原元素移过来,并且释放原空间,并更新_start,_finish,_end_of_storage。

void reserve(size_t n)
{
	if (n > capacity())
	{
		int old_size = size();
		T* temp = new T[n];
		if (_start != nullptr)
		{
			for (size_t i = 0; i < old_size; i++)
			{
				temp[i] = _start[i];
			}
			delete[] _start;
		}
		_start = temp;
		_finish = _start + old_size;
		_end_of_storage = _start + n;
	}
	else
		return;
}

2.insert函数

传入一个迭代器pos,以及一个值value。即在pos位置插入val。
当容量不够用时,即(_finish == _end_of_storage),此时需要去扩容(这里如果不为空,我们按2倍扩容)。扩容完需要更新pos的值,否则会出现迭代器失效问题。
然后从末尾依次移动元素,最后将val插入,更新_finish。
注意:最后需要将pos的值作为函数传参传回去。因为如果插入过程中有扩容行为的话,原pos就会失效。

iterator insert(iterator pos, const T& value)
{
	assert(pos<=_finish);//pos必须在有效范围内插入
	if (_finish == _end_of_storage)
	{
		size_t len = pos - _start;
		size_t new_capacity = 
		(capacity() == 0)?1 : capacity() * 2;
		reserve(new_capacity);
		pos = _start + len; // 扩容后更新
	}
	for (iterator p = _finish; p > pos; p--)
	{
		*p = *(p - 1);
	}
	*pos = value;
	++_finish;
	return pos;
}

3.erase函数

传入一个迭代器,表示删除pos位置的元素。
依次将pos后面的位置移动上来,最后更新_finish。
注意:这里需要将原pos位置传回去。如果不传,也会出现迭代器失效问题。

iterator erase(iterator pos) //删除pos位置的元素
{
	assert(pos < end()); pos也必须在有效位内
	iterator p = pos;
	while (p < end()-1)
	{
		*p = *(p + 1);
		p++;
	}
	_finish--;
	return pos;
}

4.resize函数

传入一个n
如果n小于它的size,则将原size变为n即可。
如果n大于它的size,则需扩容。并将后面的值更新为value。

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

5.push_back函数

直接调用insert即可

void push_back(const T& value)
{
	insert(_finish, value);
}

6.pop_back函数

直接调用erase函数即可

void pop_back()
{
	erase(end()-1);
}

7.swap函数

调用库里的swap依次交换即可

void swap(vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_end_of_storage, v._end_of_storage);
}

8.capacity,size,empty,front,back函数

size_t capacity()const
{
	return _end_of_storage - _start;
}

size_t size()const
{
	return _finish - _start;
}

bool empty()
{
	if (size()==0||_start == nullptr)
		return true;
	else
		return false;
}
T& front()
{
	assert(_start != nullptr);
	return *_start;
}

T& back()
{
	assert(_finish != nullptr);
	return *(_finish-1);
}

四.迭代器

1.正向迭代器

因为这里iterator本质为char* 自己就可自增自减,解引用。所以无需封装

typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
	return _start;
}

iterator end()
{
	return _finish;
}
const_iterator cbegin()const
{
	return _start;
}

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

2.反向迭代器

rbegin()返回end()
rend()返回begin()
注意每次解引用应为后一个位置

template<class T>
class vector
{
public:
class ReverseIterator{
public:
	typedef iterator reverse_iterator;
	typedef ReverseIterator self;
	ReverseIterator(iterator it)
		:_it(it)
	{}

	self& operator++()
	{
		return ReverseIterator(--_it);
	}

	T& operator*()
	{
		return *(_it - 1);
	}
	self& operator--()
	{
		return ReverseIterator(++_it);
	}

	bool operator!=(self& it)
	{
		return _it != it._it;
	}
private:
	reverse_iterator _it;
};
public:
typedef ReverseIterator reverse_iterator;
typedef const ReverseIterator const_reverse_iterator;
reverse_iterator rbegin()
{
	return end();
}
reverse_iterator rend()
{
	return begin();
}
};

总结

以上就是vector的常用函数以及模拟实现(本人小白一枚,若有错误还望各位指正)
完整的代码存放在gitee上:https://gitee.com/jj-wen/c-beginner/blob/master/06.20%EF%BC%88%E5%A4%8D%E4%B9%A0vector%EF%BC%89/06.20%EF%BC%88%E5%A4%8D%E4%B9%A0vector%EF%BC%89/vector.h
学习任重而道远,希望自己可以坚持下去。

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用和提供了关于实现vector的两种方法。其中,引用展示了一个使用reserve和push_back方法的示例,而引用展示了一个使用new和memcpy函数的示例。这两种方法都是常见实现vector的方式。 在第一种方法中,通过reserve函数可以预留足够的内存空间,然后使用push_back函数逐个将元素添加到vector中。这种方法的好处是可以避免不必要的内存重分配,提高了效率。 而第二种方法使用new操作符在堆上分配内存空间,并使用memcpy函数将已有的vector对象的数据复制到新的内存空间中。通过这种方式,可以实现深拷贝,即两个vector对象拥有独立的内存空间。这种方法的好处是可以在不修改原始vector对象的情况下创建一个新的vector对象。 除了以上两种方法,还可以使用其他方式实现vector。例如,可以使用动态数组来实现vector的底层数据结构,然后通过成员函数实现vector的各种操作,如增加、删除、查找等。 总结来说,c语言模拟实现vector的关键是动态内存管理和对元素的增删改查操作。可以使用预留空间和逐个添加元素的方式,也可以使用动态数组和复制数据的方式来实现vector。具体的实现方式可以根据需求和实际情况选择。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++——vector模拟实现](https://blog.csdn.net/weixin_49449676/article/details/126813526)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值