STL?vector!!!

发布于:2025-05-09 ⋅ 阅读:(14) ⋅ 点赞:(0)

一、前言

        之前我们借助手撕string加深了类和对象相关知识,今天我们将一起手撕一个vector,继续深化类和对象、动态内存管理、模板的相关知识

二、vector相关的前置知识

        1、什么是vector?


        vector是一个STL库中提供的类模板,它是存储元素对象的顺序表,其中提供了一些有关增删查改的接口,它的特点是可以通过下标的方式在表中的任意位置进行读、写

        2、vector中的相关接口

        在本文接下来的部分会介绍vector的常用接口,事实上借助这些接口就可以解决平常所能遇到的大部分问题,如果还需要了解vector提供的更多接口及使用方法的话,可以跳转到一下网页:
        vector - C++ Referencehttps://legacy.cplusplus.com/reference/vector/vector/?kw=vector

三、手撕一个vector类

        1、成员变量与整体框架

        注意:之前的顺序表我们都是通过记录指针、元素个数和空间大小来完成的,顺序表的实现自然也可以这样,但是STL库中不是这样实现的,所以今天我们学习一下STL库中的实现方式

        

        上面的_start、_finish、_endofstorge分别代表着指向有效元素起始位置、终止位置的下一个位置和空间终止位置的迭代器,所以在上面我们已经定义了迭代器,事实上就是指针类型,这是由于顺序表的一大特点就是在内存中连续存储,所以可以直接使用未封装的指针;另一方面,我们在声明成员变量的同时都给了一个缺省值,这是由于它们都是指针类型,在初始化时我们都要先初始化成空指针再进行下一步操作,所以我们可以直接在这里给一个缺省值,这样避免了在初始化列表多次的显示初始化成空指针

        2、构造函数

        库中的函数头:
        

        以上的三个构造函数我们都会一一实现,在这里补充一点:在上面的构造函数中出现了"clloc",事实上,这是一个内存池,而后面所给的缺省值是STL库中提供的一个默认的内存池,它可以有效的提高开辟空间的时间消耗,但是比较复杂,所以在这里我们直接new空间,在之后,我们会专门的进行讲解

                (1).空构造:
                

                在这里实现的空构造是非常简单的,这是由于我们在声明时已经给了缺省值,但是该空构造是一定要写的,这是因为我们还要实现别的构造函数,这时候编译器就不在自动生成默认构造函数了

                (2).用n个元素对象进行初始化:
                

                在使用n个元素对象进行初始化时,我们在参数部分给了一个缺省值T(),很明显,这是一个匿名对象,同时调用了对应的默认构造函数,这是没有问题的,但是如果T是int、float等内置类型呢?事实上,这点不用担心,这是由于C++将这些内置类型都进行了升级,使它们可以象自定义类型一样调用默认构造函数,举个例子:如果T是int类型,那么此时T()就会返沪一个0进行初始化

                (3).使用一个迭代器区间进行初始化:
                

                在上面的构造函数中,我们再次使用了模板,但这里是函数模板,这是由于我们不仅要支持顺序表的迭代器类型初始化,还要支持其它类型的迭代器初始化

        3、返回顺序表一些性质的函数

                (1).size函数:返回此时vector中的元素个数

                库中的函数头:
                

                实现:
                

                (2).capacity函数:返回此时vector中的空间大小

                库中的函数头:
                

                实现:
                

        4、拷贝构造函数

        库中的函数头:

        

        实现:
        

        这里需要注意:我们不可以使用memcpy,这是由于memcpy会按字节将x的内容拷贝到_start,但如果T类型是动态开辟内存,也就是说是深拷贝的话,那么虽然vector整体进行了深拷贝,但是vector中的内容却只完成了浅拷贝,会出现深浅拷贝的问题,此时选择上面的方式就万无一失了,这要求元素对象重载了赋值运算符,这一点是必须的

        5、迭代器相关函数

                (1).begin函数:


                库中的函数头:
                

                begin函数进行了两个重载,分别是普通begin和const begin它们分别可以返回一个可读可写的迭代器和一个只读不写的迭代器,都指向vector的首元素位置:
                

                (2).end函数:


                库中的函数头:
                

                end函数仍然进行了两个重载,返回的迭代器都指向末尾元素的下一个位置:
                

        6、[]操作符重载

        库中的函数头:
        

        库中对[]操作符进行了两个重载,与上面的begin类似:
        

        库中对于很多类型进行了封装,事实上上面我们实现的与库中的本质是一样的

        7、交换函数

        库中的函数头:
        

        这里的交换一定是要涉及深拷贝的,在这里我们借用一下算法库中的swap函数,因为它恰好就是深拷贝:
        

        8、赋值运算符重载

        库中的函数头:
        

        在这里我们对该函数实现进行一点小改动,但是对于用户的使用来说是相同的:
        

        在上面的实现中,我们采用了传值传参,此时形参是实参的拷贝,拷贝会调用我们之前实现过的拷贝构造函数进行深拷贝,再调用swap函数,它也会进行深拷贝之下的交换,最终*this就变成了x的拷贝,最后x这一拷贝出作用域销毁,调用析构函数(后面会实现),进行了空间的释放

        9、开空间相关函数

                (1).reserve函数:会开辟指定大小的空间,如果原来有元素,会进行拷贝,否则不进行任何处理

                库中的函数头:
                

                实现:
                

                在上面我们又遇到了象拷贝构造函数同样的问题,所以再次使用了赋值运算符重载的方式进行处理

                (2).resize函数:会开辟指定大小的空间,用val进行初始化,缺省值为T()

                库中的函数头:
                

                实现:
                

                这里我们直接对之前的函数进行复用即可

        10、插入相关函数

                (1).insert函数:在迭代器指定的位置直接插入一个值,返回最后该位置的迭代器

                库中的函数头:
                

                实现:
                

                从上面的代码可以看出:在insert函数内部可能开辟空间,这时候pos就不是原来的pos了,所以在函数外部,传给过insert函数的迭代器是不能再次使用的,因为使用过的迭代器可能已经失效,这时候我们通过反回值的方式解决了这个问题,所以如果想再次使用的话,要接受函数的返回值

                (2).push_back函数:在vector尾部插入一个对象

                库中的函数头:
                

                实现:
                

                事实上在这个位置我们直接复用刚才写过的insert就非常方便

        11、删除相关函数

                (1).erase函数:删除迭代器指定位置的元素,返回该位置的迭代器

                库中的函数头:
                

                实现:
                

                erase所删除的位置有可能是最后一个,此时删除之后传入的迭代器·就失效了,所以要接收返回值并判断

                (2).pop_back函数

                库中的函数头:

                

                实现:
                

                直接复用刚才写过的erase函数即可

        12、析构函数

        由于析构函数的特殊性,这里就不提供库中的函数头了:

        

四、vector

        下面就是我们今天一起完成的vector了:
        

#include <iostream>
#include <cassert>
#include <algorithm>
using namespace std;
namespace bea
{
	template<class T>
	class vector
	{
		typedef T* iterator;
		typedef const T* const_iterator;
	public:
		//空构造
		vector() {}
		//用n个元素对象进行初始化
		vector(size_t n, const T& val = T())
		{
			_start = new T[n + 5];
			for (size_t i = 0; i < n; i++)
			{
				_start[i] = val;
			}
			_finish = _start + n;
			_endofstorge = _start + n + 4;
		}
		//使用一个迭代器区间进行初始化
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			size_t n = last - first;
			_start = new T[n + 5];
			InputIterator it1 = first, it2 = _start;
			while (it1 < last)
			{
				*it2 = *it1;
				it1++, it2++;
			}
			_finish = _start + n;
			_endofstorge = _start + n + 4;
		}
		//拷贝构造函数
		vector(const vector& x)
		{
			size_t sz = x.size();
			_start = new T[sz + 5];
			for (size_t i = 0; i < sz; i++)
			{
				_start[i] = x._start[i];
			}
			_finish = _start + sz;
			_endofstorge = _start + sz + 4;
		}

		//size函数
		size_t size() const
		{
			return _finish - _start;
		}
		//capacity函数
		size_t capacity() const
		{
			return _endofstorge - _start + 1;
		}
		//begin函数
		iterator begin()
		{
			return _start;
		}
		const_iterator begin() const
		{
			return _start;
		}
		//end函数
		iterator end()
		{
			return _finish;
		}
		const_iterator end() const
		{
			return _finish;
		}
		//[]操作符重载
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}
		const T& operator[](size_t pos) const
		{
			assert(pos < size());
			return _start[pos];
		}
		//交换函数
		void swap(vector& x)
		{
			std::swap(x._start, _start);
			std::swap(x._finish, _finish);
			std::swap(x._endofstorge, _endofstorge);
		}
		//赋值运算符重载
		vector& operator=(const vector x)
		{
			swap(x);
			return *this;
		}
		//开空间相关函数
		void reserve(size_t n)
		{
			size_t sz = size();
			if (n <= size()) return;
			iterator tmp = new T[n + 5];
			if (_start)
			{
				for (size_t i = 0; i < sz; i++)
				{
					tmp[i] = _start[i];
				}
				delete[] _start;
			}
			_finish = tmp + sz;
			_start = tmp;
			_endofstorge = tmp + n + 4;
		}
		//insert函数
		iterator insert(iterator pos, const T& val)
		{
			assert(pos <= _finish);
			int n = pos - _start;
			if (capacity() < size() + 1)
			{
				reserve(size() + 5);
			}
			pos = _start + n;
			iterator end = _finish - 1, next = _finish;
			while (next > pos)
			{
				*next = *end;
				end--, next--;
			}
			*pos = val;
			_finish++;
			return pos;
		}
		void push_back(const T& val)
		{
			insert(_finish, val);
		}
		void resize(size_t n, T& val = T())
		{
			vector(n, val);
		}
		//erase函数
		iterator erase(iterator pos)
		{
			assert(pos < _finish);
			iterator end = pos + 1, front = pos;
			while (end < _finish)
			{
				*front = *end;
				front++, end++;
			}
			_finish--;
			if (pos == _finish) return nullptr;
			else return pos;
		}
		//pop_back函数
		void pop_back()
		{
			erase(_finish - 1);
		}
		//析构函数
		~vector()
		{
			delete[] _start;
			_start = nullptr;
			_finish = nullptr;
			_endofstorge = nullptr;
		}
	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _endofstorge = nullptr;
	};
}

五、结语

        这就是我们所实现的vector的全部内容了,我们的目的仍然是了解vector的用法、加深类和对象和模板等知识点的理解,感谢大家的阅读,欢迎各位于晏、亦菲和我一起交流、学习、进步!

                

               


网站公告

今日签到

点亮在社区的每一天
去签到