C++模拟实现vector

前言

在本文中,我们的目标是使用C++以及模板的知识来对vector进行模拟实现以及测试。

一、基本框架

为了避免命名冲突,我们vector的实现是在命名空间myvector中的,我们创建两个文件,vector.h以及test.cpp,前者来实现vector,后者来对我们实现的vector进行测试。

通过查看vector的底层源代码,我们可以得知:

1.vector中有三个成员:_start,_finish,_end_of_storge;

2._start代表vector开始的地址,_finish代表数据存储结束的后一个位置,_end_of_storge代表vector后的一个位置

二、功能预览

在模拟实现vector中,我们需要实现以下功能:


namespace myvector {

	template<typename T>
	class vector {
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
	private:
		T* _start;
		T* _finish;
		T* _end_of_storge;
	public:
        //构造函数与析构函数
		vector();
		vector(size_t n, const T& value = T());
		vector(int n, const T& value = T());	
		template<class InputIterator>
		vector(InputIterator first, InputIterator last);
		vector(const vector<T>& v);
		vector<T>& operator=(vector<T> v);
		vector<T>& operator=(initializer_list<T> l);
		~vector()

        //迭代器
		iterator begin(); 
		iterator end(); 
		const_iterator begin()const; 
		const_iterator end()const; 

		//容量相关
		size_t size()const; 
		size_t capacity()const; 
		void reserve(size_t n); 
		void resize(size_t n, const T& value = T()); 
		T& operator[](size_t pos);
		const T& operator[](size_t pos)const; 
        
        //修改操作
		void push_back(const T& x); 
		void pop_back(); 
		void swap(vector<T>& x); 
		iterator insert(iterator pos, const T& x); 
		iterator erase(iterator pos); 

	};
}

三、构造函数与析构函数

1.构造函数

我们提供了四种构造函数

1) 无参构造
vector()
	: _start(nullptr)
	, _finish(nullptr)
	, _end_of_storge(nullptr){
}

作用:不传参的默认构造函数

实现:将_start,_finiish,_end_of_storge指向空指针,表示此时vector为空

2)使用n个T类型对象进行构造
vector(size_t n, const T& value = T())
	: _start(nullptr)
	, _finish(nullptr)
	, _end_of_storge(nullptr){
	reserve(n);
	while (n--){
		push_back(value);
	}
}

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

注意:这里分别用size_t和int类型的n实现一次的原因是对于 vector<int> v(10, 5);这样的构造参数,编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型就不会走vector(size_t n, const T& value = T())这个构造方法, 最终选择的是:vector(InputIterator first, InputIterator last),因为编译器认为区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int,但是10和5根本不是一个区间,编译时就报错了故需要增加该构造方法。

3)迭代器构造
		template<class InputIterator>
		vector(InputIterator first, InputIterator last){
			while (first != last){
				push_back(*first);
				++first;
			}
		}

说明:传入两个迭代器,将二者之间的元素 填入vector中

4)拷贝构造函数
vector(const vector<T>& v)
	: _start(nullptr)
	, _finish(nullptr)
	, _end_of_storge(nullptr){
	reserve(v.capacity());
	iterator it = begin();
	const_iterator vit = v.begin();
	while (vit != v.end()){
		*it++ = *vit++;
	}
	_finish = it;
}
5)赋值重载函数
vector<T>& operator=(vector<T> v){

	swap(v);
	return *this;
}

注意:这里的swap并不会和传入的vector交换,因为传入的参数v是传值调用,v是传入的vector对象的一份临时拷贝。所以并不会改变原来的vector对象。

2.析构函数

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

实现:释放_start指向的空间,将_start,_finish,_end_of_storge置空。

3.测试

我们通过以下代码来进行测试

预期结果:编译通过并且无程序崩溃。

//测试构造函数与析构函数
void test1() {
	myvector::vector<int>v1;
	myvector::vector<int>v2(5, 10);
	myvector::vector<int>v3(v2);
	myvector::vector<int>v4(v3.begin(), v3.end());
	myvector::vector<int>v5 = v4;
}

运行结果:

 通过!!!

四、迭代器函数

注意:在这里,迭代器分两种,一种是可修改的迭代器,一种是不可修改的迭代器,所以我们需要写两套迭代器函数。迭代器的命名不得随意修改,必须使用规定的命名,否则无法使用快捷for循环遍历。

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

 测试

我们通过以下代码对迭代器相关函数进行测试

预期结果:v1打印12个7,v2打印12个6

/测试迭代器函数
void test2(){

	myvector::vector<int>v1(12,6);
	const myvector::vector<int>v2 = v1;
	myvector::vector<int>::iterator it1 = v1.begin();
	while(it1 != v1.end()) {
		(*it1)++;
		cout << *it1 << " ";
		it1++;
	}
	cout << endl;

	myvector::vector<int>::const_iterator it2 = v2.begin();
	while (it2 != v2.end()) {
		cout << *it2 << " ";
		it2++;
	}
	cout << endl;
	
}

 通过!!!

五、大小容量相关函数

1)size()函数        

作用:获取数据大小

实现:使用_finish直接与_start相减得到两个指针之间间隔的元素个数,即为元素个数

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

2)capacity()函数

作用:获取vector当前容量大小

实现:使用_end_of_storge直接与_start相减得到两个指针之间间隔的距离,即为容量大小

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

3)reserve()函数

作用:将vector容量扩容至n,若n<capacity(),则不进行操作

实现:先记录当前vector中元素的个数,以防后续无法确定_finish的位置,申请一块大小为n的新空间,将原来的数据拷贝到新的空间中,再将原来的_start指向新的空间,更新_finish和_end_of_stroge。

void reserve(size_t n) {
	if (_end_of_storge - _start < n) {
		size_t old_size = _finish - _start;
		T* tmp = new T[n];	
		if (tmp) {
			memcpy(tmp, _start, sizeof(T) * (_end_of_storge - _start));
			delete[]_start;
			_start = tmp;
		}
		_finish = _start + old_size;
		_end_of_storge = _start + n;
	}
}

4)resize()函数

作用:将vector的大小调整为n,如果原来的元素个数多于n,则将元素个数删至n,如果原来的元素个数小于n,则在原来的数据后面填充元素value,直至元素个数为n

实现:如果n<size(),直接将_finish调整为_start+n,否则从_finish+1开始,填充元素value,直到元素个数为n。

void resize(size_t n, const T& value = T()) {
	if (n < _finish - _start) {
		_finish = _start + n;
		return;
	}
	if (n > _end_of_storge - _start) {
		reserve(n);
	}
	iterator t = _finish;
	_finish = _start + n;
	while (t != _finish) {
		*t = value;
		++t;
	} 
}

5)[]重载

作用:返回第n个元素

T& operator[](size_t pos) {
	assert(pos < size());
	return _start + pos;
}

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

注意:由于const修饰的对象也需要使用[]访问,并且使用const修饰的对象访问的元素不能被修改,所以我们需要两个不同的[]重载。

测试

我们通过以下的代码进行测试:

void test3() {
	myvector::vector<int>v5(10, 2);
	myvector::vector<string>v6(20, "helle world!");
	cout << "v5:capacity:" << v5.capacity()<<"size():"<<v5.size() << endl;
	cout << "v6:capacity:" << v6.capacity()<<"size():"<<v6.size() << endl;

	v5.reserve(10);
	cout << "v5:capacity:" << v5.capacity() << "size():" << v5.size() << endl;
	v6.resize(12,"hehe");
	cout << "v6:capacity:" << v6.capacity() << "size():" << v6.size() << endl;

	for (int i = 0; i < v5.size();i++) {
		cout << v5[i] << " ";
	}
	cout << endl;

}

预期结果:按照函数作用输出结果

通过!!!

六、操作函数

1)push_back()函数

作用:在vector的末尾插入一个元素

实现:先检查vector是否满了,如果满了需要进行扩容操作,然后将_finisgh位置赋值为x,_finish后移一位即可。

void push_back(const T& x) {
	
	if (_finish == _end_of_storge) {
		reserve(_end_of_storge - _start == 0 ? 4 : 2 * (_end_of_storge - _start));
	}

	*_finish = x;
	++_finish;
}

2)pop_back()函数

作用:如果vector不为空,则将vector最后一个元素删除

实现:将_finish前移一位即可

void pop_back() {
	if (_finish != _start) {
		--_finish;
	}
}

3)swap()函数

作用:交换两个vector的内容

实现:调用stl库中的swap将_start,_finish,_end_of_storge分别交换即可

void swap(vector<T>& x) {
	std::swap(this->_start, x._start);
	std::swap(this->_finish, x._finish);
	std::swap(this->_end_of_storge, x._end_of_storge);
}

4)insert()函数

作用:在pos位置擦插入一个元素

实现:先检查vector需不需要扩容,再将pos后面的元素全部后移一个位置,将pos位置的值改为x,再将_finish往后移一个位置即可

iterator insert(iterator pos, const T& x) {
	assert(pos <= _finish);
	// 空间不够先进行增容
	if (_finish == _end_of_storge)
	{
		//size_t size = size();
		size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;
		reserve(newCapacity);

		// 如果发生了增容,需要重置pos
		pos = _start + size();
	}

	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end;
		--end;
	}

	*pos = x;
	++_finish;
	return pos;
}

5)erase()函数

作用:删除pos位置的元素

实现:将pos后面的元素前移一个位置即可

iterator erase(iterator pos) {
	// 挪动数据进行删除
	iterator begin = pos + 1;
	while (begin != _finish) {
		*(begin - 1) = *begin;
		++begin;
	}

	--_finish;
	return pos;
}

测试

我们通过以下代码进行测试:
预期结果:按照函数作用输出结果

void test4() {
	myvector::vector<int>v1(10, 2);
	for (auto e : v1) {
		cout << e << " ";
	}
	cout << endl;

	v1.push_back(4);
	for (auto e : v1) {
		cout << e << " ";
	}
	cout << endl;

	v1.pop_back();
	for (auto e : v1) {
		cout << e << " ";
	}
	cout << endl;

	v1.insert(v1.begin(),3);
	for (auto e : v1) {
		cout << e << " ";
	}
	cout << endl;

	v1.erase(v1.begin());
	for (auto e : v1) {
		cout << e << " ";
	}
	cout << endl;
	
	myvector::vector<int>v2(2, 7);
	v1.swap(v2);
	for (auto e : v1) {
		cout << e << " ";
	}
	cout << endl;
}

通过!!!

  • 29
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值