C++ vector模拟实现

                                              C++ vector模拟实现_第1张图片

        前几天写了一篇vector的使用,这次来讲一下vector的模拟实现。我们不仅要学习STL库的使用,还要了解它的底层,有助于我们更好的记忆和未来更好的学习。所以我们不能只停留在STL的使用层面上,今天带大家来走进vector的底层~

       当然我们模拟实现vector并不是不是造更好的轮子,我们是为了了解它,学习它~

目录

成员变量定义

 成员函数

vector()

vector(InputIterator first, InputIterator last)

vector(vector& v)

void swap(vector& v)

vector& operator=(vector v)

~vector()

const_iterator begin()const

const_iterator end()const 

iterator begin()

iterator end()

T& operator[](size_t i)

const T& operator[](size_t i) const

size_t size()

size_t capacity()

void reserve(size_t n)

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

iterator insert(iterator pos, const T& x)

iterator erase(iterator pos)

void push_back(const T& x)

void pop_back()

完整代码

vector.h

测试test.cpp


成员变量定义

public:
	typedef T* iterator;
	typedef const T* const_iterator;	
private:
	iterator _start;  //T* 可以修改不要 const_iterator
	iterator _finish;
	iterator _endofstorage;

        这个和之前讲的string有些不一样,在这里使用指针来控制起始位置、最后一个数据位置、最大容量位置。那我们就简单看一下它的内存结构~

                   C++ vector模拟实现_第2张图片

C++ vector模拟实现_第3张图片

 我这是参考侯捷老师《STL原码剖析》这本书画图的,书上的展示图如下:

C++ vector模拟实现_第4张图片

 成员函数

vector()

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

    vector()  //构造函数
        :_start(nullptr) 初始化把成员变量置空,表示构造出来的对象刚开始还没有被使用
        ,_finish(nullptr)
        ,_endofstorage(nullptr)
    {}

vector(InputIterator first, InputIterator last)

	//一个类模板的成员函数,又可以是一个函数模板
	template
	vector(InputIterator first, InputIterator last)
		:_start(nullptr)
		,_finish(nullptr)
		,_endofstorage(nullptr)
	{
		while (first != last)
		{
			push_back(*first); //在这里不用考虑new多大空间
			++first;
		}
	}

    //一个类模板的成员函数,又可以是一个函数模板
    template  //InputItrtator是定义的一个迭代器类型

C++ vector模拟实现_第5张图片
    vector(InputIterator first, InputIterator last) //用迭代器进行构造函数
        :_start(nullptr)  //构造函数之前还是要把下面3个成员函数都置成nullptr,因为这里也是一个构造函数,调用了这个,那么上面的构造函数就不会走了。
        ,_finish(nullptr)
        ,_endofstorage(nullptr)
    {
        while (first != last) //当初始位置的迭代器不等于最后一个数据下一个位置的迭代器时,就继续走入循环。---> last代表最后一个数据的下一个位置的迭代器
        {
            push_back(*first); //在这里不用考虑new多大空间,push_back里面把细节解决过了。不断往要构造的对象里面插入数据。 
            ++first; //first往后走,更新位置
        }
    }

vector(vector& v)

	//v2(v1) v2是未初始化的对象
	//传统写法
	vector(vector& v)
	{
		_start = new T[v.capacity()]; //这里要开辟容量的空间大小
		_finish = _start + v.size(); //指定里面存到的数据位置
		_endofstorage = _start + v.capavity();

		memcpy(_start, v._start, v.size() * sizeof(T));  //注意这里有一个Bug
	}

	//v2(v1)
	//现代写法
	//vector(const vector& v)
	vector(vector& v)
		:_start(nullptr)
		, _finish(nullptr)
		, _endofstorage(nullptr)
	{
		vector tmp(v.begin(), v.end()); //调用构造函数
		swap(tmp);
	}

    //v2(v1) v2是未初始化的对象
    //传统写法
    vector(vector& v)
    {
        _start = new T[v.capacity()]; //这里要开辟容量的空间大小
        _finish = _start + v.size(); //指定里面存到的数据位置
        _endofstorage = _start + v.capavity(); //vector开辟的空间大小

        memcpy(_start, v._start, v.size() * sizeof(T));  //注意这里有一个Bug 如果我们要修改的的话就用赋值的方式,string对象就会调用自己深拷贝赋值完成拷贝(这里博主就不实现了,大家可以尝试一下,如果没有想到的话可以私信我哦~)

如果vector里面存的是内置类型这种写法是没有问题的,但是对于自定义类型是不正确的。

       memcpy是按照字节序拷贝,若果要拷贝的内容时string类的对象,那么每次拷贝都是把对应的_str拷贝过去了,这时候就存在一个问题,两个vector对象存了同一个在堆上开辟空间的地址。这就会造成在调用~vector()时会对同一块空间析构两次,这时候程序会崩溃!

C++ vector模拟实现_第6张图片
    } 

-----------------------------------------------------------------------------------------------------------------------

    //v2(v1)
    //现代写法
    //vector(const vector& v)
    vector(vector& v)
        :_start(nullptr)  //构造前把三个成员变量进行置空初始化
        , _finish(nullptr)
        , _endofstorage(nullptr)
    {
        vector tmp(v.begin(), v.end()); //复用构造函数
        swap(tmp);//将构造好的临时对象和要构造的对象进行交换(这里的swap是自己实现的,实际上就是改变指针的指向来控制资源的交换,并没有涉及深拷贝代价太大的问题)
    }

void swap(vector& v)

	void swap(vector& v)
	{
		std::swap(_start, v._start);
		std::swap(_finish, v._finish);
		std::swap(_endofstorage, v._endofstorage);
	}

    void swap(vector& v)
    {
        std::swap(_start, v._start);  //在这里的swap是库里面的swap,如果不指定std::或者::时,就会出现死递归的情况,因为swap(_start,v._start)的调用会就近原则,不断调用自己的swap,最后因为栈溢出导致程序崩溃。
        std::swap(_finish, v._finish);
        std::swap(_endofstorage, v._endofstorage);
    }

vector& operator=(vector v)

	//v1 = v3
	//现代写法
	//vector& operator=(vector v)
	vector& operator=(vector v)  //this不是临时对象
	{
		swap(v);
		return *this;
	}

    //v1 = v3
    //现代写法
    //vector& operator=(vector v)
    vector& operator=(vector v)  //this不是临时对象所以传引用返回。这里在传参的时候是复用了拷贝构造,所以这里v是深拷贝过的临时对象。
    {
        swap(v); 将被赋值的对象和新构造的临时对象进行swap交换资源,完之后v拿到了被赋值对象的_start、_finish、_endofstorage,这时候v出了该作用域就会调用自己的析构函数,完成对应的资源清理。(就好比这个临时对象就是干苦力的,假设v是一个外卖小哥,他辛苦的把饭带给客户(在这里就相当于swap交换资源)后,又被客户要求把他们家的垃圾袋待下去(在这里就相当于对v1内资源的清理)~)
        return *this; //返回被赋值好后的对象
    }

~vector()

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

    ~vector() //析构函数
    {
        if (_start) //如果_start不为空时就开始进行下面的资源清理,当然这里不判断也是可以的
        {
            delete[] _start; //delete释放资源,析构函数(如果对内部有自定义类型而言,调用其内部的析构函数) + operator delete 
            _start = _finish = _endofstorage = nullptr; //三个变量置空
        }
    }

const_iterator begin()const

const_iterator begin()const //权限缩小
{
	return _start;
}

const_iterator end()const 

const_iterator end()const  //权限缩小
{
	return _finish;
}

iterator begin()

iterator begin()
{
	return _start;
}

iterator end()

iterator end()
{
	return _finish;
}

       上面四个迭代器实现比较简单,在之前string模拟实现的博客中有详细介绍,所以在这里就不多费话了~

T& operator[](size_t i)

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

    T& operator[](size_t i)
    {
        assert(i < size()); //保证所用的下标有对应的数据
        return _start[i]; //[]相当于解引用,返回的对象可读可写
    }

const T& operator[](size_t i) const

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

    const T& operator[](size_t i) const
    {
        assert(i < size());
        return _start[i];  //返回的对象是只读的,不可以被修改
    }

size_t size()

	size_t size()
	{
		return _finish - _start;
	}

    size_t size()
    {
        return _finish - _start; //指针 - 指针等于元素个数,对于下标控制问题,之前也在string的使用的find和substr功能时(可以去找找哦~),详细讲过类似的,如果是铁粉的话肯定知道的~ 
    }

size_t capacity()

	size_t capacity()
	{
		return _endofstorage - _start;
	}

    size_t capacity()
    {
        return _endofstorage - _start; //计算vector开辟的容量大小
    }

void reserve(size_t n)

	void reserve(size_t n)
	{
		if (n > capacity())
		{
			size_t sz = size(); //提前记录原来空间sz的大小
			T* tmp = new T[n];
			if (_start) //有数据就拷贝
			{
				for (size_t i = 0; i < sz; i++)
				{
					tmp[i] = _start[i];
				}
				delete[] _start;
			}
			_start = tmp; //注意这个地方_start位置变了,不是原来的_start,size()、capacity() 已经失效了
			_finish = _start + sz;
			_endofstorage = _start + n;
		}
	}

    void reserve(size_t n)
    {
        if (n > capacity()) //如果要开辟的空间大于当前容量就执行
        {
            size_t sz = size(); //提前记录原来空间sz的大小
            T* tmp = new T[n]; //开辟空间
            if (_start) //有数据就拷贝
            {
                for (size_t i = 0; i < sz; i++) //注意,拷贝数据不能memcpy!原因呢在上面讲构造函数时讲过了~
                {
                    tmp[i] = _start[i]; //如果是像stack这样的类就是深拷贝
                }
                delete[] _start; //释放旧空间
            }
            _start = tmp; //注意这个地方_start位置变了,不是原来的_start,size()、capacity() 已经失效了,在这个在下面就不能在使用size()、capacity()了。
            _finish = _start + sz; //sz已经提前保存过了
            _endofstorage = _start + n; //n代表实际能存数据的个数(容量)
        }
    }

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

	void resize(size_t n, const T& val = T())
	{
		if (n < size())
		{
			_finish = _start + n; //截取数据
		}
		else
		{
			if (n > capacity()) //要扩的空间大于当前空间 --- 扩容
			{
				reserve(n);
			}
			while (_finish != _start + n)
			{
				*_finish = val;  //上面扩过容的,所以可以这么写
				++_finish;
			}
		}
	}

    void resize(size_t n, const T& val = T())
    {
        if (n < size()) //要扩的空间(n)小于当前数据个数(size())
        {
            _finish = _start + n; //截取数据
        }
        else//要扩的空间(n)大于当前数据个数(size())
        {
            if (n > capacity()) //要扩的空间(n)大于当前空间(capacity()) --- 扩容
            {
                reserve(n);
            }
            while (_finish != _start + n) //直到第n个数据就停止
            {
                *_finish = val;  //上面扩过容的,所以可以这么写,不用考虑越界问题
                ++_finish;
            }
        }
    }

iterator insert(iterator pos, const T& x)

	iterator insert(iterator pos, const T& x)
	{
		assert(pos >= _start);
		assert(pos <= _finish);

		//满了就扩容
		if (_finish == _endofstorage)
		{
			//扩容会导致pos失效,扩容需要更新一下pos
			size_t len = pos - _start;
			reserve(capacity() == 0 ? 4 : capacity() * 2);
			pos = _start + len;
		}
		iterator end = _finish - 1;
		while (end >= pos)
		{
			*(end + 1) = *end; //不能使用_start[end]...
			end--;
		}
		*pos = x;
		++_finish;
		return pos;
	}

    iterator insert(iterator pos, const T& x)
    {
        assert(pos >= _start);
        assert(pos <= _finish);

        //满了就扩容
        if (_finish == _endofstorage)
        {
            //扩容会导致pos失效,扩容需要更新一下pos
            size_t len = pos - _start; //提前记录pos在原来空间中是第几个数据
            reserve(capacity() == 0 ? 4 : capacity() * 2);
            pos = _start + len; //在新的空间中pos映射到对应的第len个数据
        }
        iterator end = _finish - 1;
        while (end >= pos)  //挪动数据,之前在讲string模拟实现也详细讲过了(大致类似)~
        {
            *(end + 1) = *end; //不能使用_start[end]...
            end--;
        }
        *pos = x;
        ++_finish;
        return pos;//返回新插入数据的迭代器,注意pos是形参,上面的pos = _start + len并没有改变pos!!!
    }

iterator erase(iterator pos)

	iterator erase(iterator pos)
	{
		assert(pos >= _start);
		assert(pos < _finish);

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

void push_back(const T& x)

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

void pop_back()

void pop_back()
{
	assert(_start < _finish);
	--_finish;
}

    void pop_back()
    {
        assert(_start < _finish);
        --_finish; //直接把最后一个数据的指向改变就行了(_finish指向最后一个数据的下一个位置的迭代器)
    }

完整代码

vector.h

在这里vector模拟实现放在了cyq的命名空间中,是为了防止命名冲突。

#pragma once
#include
#include
#include
using namespace std;

namespace cyq
{
	template
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{}

		//v2(v1) v2是未初始化的对象
		//传统写法
		//vector(vector& v)
		//{
		//	_start = new T[v.capacity()]; //这里要开辟容量的空间大小
		//	_finish = _start + v.size(); //指定里面存到的数据位置
		//	_endofstorage = _start + v.capavity();

		//	memcpy(_start, v._start, v.size() * sizeof(T));  //注意这里有一个Bug
		//}

		//一个类模板的成员函数,又可以是一个函数模板
		template
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			while (first != last)
			{
				push_back(*first); //在这里不用考虑new多大空间
				++first;
			}
		}
		void swap(vector& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}
		//v2(v1)
		//现代写法
		//vector(const vector& v)
		vector(vector& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			vector tmp(v.begin(), v.end()); //调用构造函数
			swap(tmp);
		}
		//v1 = v3
		//现代写法
		//vector& operator=(vector v)
		vector& operator=(vector v)  //this不是临时对象
		{
			swap(v);
			return *this;
		}
		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _endofstorage = nullptr;
			}
		}
		const_iterator begin()const //权限缩小
		{
			return _start;
		}
		const_iterator end()const  //权限缩小
		{
			return _finish;
		}
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		T& operator[](size_t i)
		{
			assert(i < size());
			return _start[i];
		}
		const T& operator[](size_t i) const
		{
			assert(i < size());
			return _start[i];
		}
		size_t size()
		{
			return _finish - _start;
		}
		size_t capacity()
		{
			return _endofstorage - _start;
		}
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size(); //提前记录原来空间sz的大小
				T* tmp = new T[n];
				if (_start) //有数据就拷贝
				{
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete _start;
				}
				_start = tmp; //注意这个地方_start位置变了,不是原来的_start,size()、capacity() 已经失效了
				_finish = _start + sz;
				_endofstorage = _start + n;
			}
		}
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n; //截取数据
			}
			else
			{
				if (n > capacity()) //要扩的空间大于当前空间 --- 扩容
				{
					reserve(n);
				}
				while (_finish != _start + n)
				{
					*_finish = val;  //上面扩过容的,所以可以这么写
					++_finish;
				}
			}
		}
		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);

			//满了就扩容
			if (_finish == _endofstorage)
			{
				//扩容会导致pos失效,扩容需要更新一下pos
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end; //不能使用_start[end]...
				end--;
			}
			*pos = x;
			++_finish;
			return pos;
		}
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);

			iterator end = pos + 1;
			while (end < _finish)
			{
				*(end - 1) = *end;
				end++;
			}
			--_finish;
			return pos;
		}
		void push_back(const T& x)
		{
			if (_finish == _endofstorage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = x;
			++_finish;
		}
		void pop_back()
		{
			assert(_start < _finish);
			--_finish;
		}
	private:
		iterator _start;  //T* 可以修改不要 const_iterator
		iterator _finish;
		iterator _endofstorage;
	};
}

测试test.cpp

void TestVector()
{
	cyq::vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "capacity: " << v.capacity() << endl;
	v.erase(v.begin());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	v.insert(v.begin()+2,40);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	v.resize(20, 10);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	v.reserve(30);
	cout << v.capacity() << endl;
	cyq::vector::iterator it = v.begin();
	//auto it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		it++;
	}
	cout< v1(v.begin(),v.end());
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
	cyq::vector v2(v);
	v2.push_back(100);
	v2.push_back(100);
	v2.pop_back();
	for (auto e : v2)
	{
		cout << e << " ";
	}
}
int main()
{
	TestVector();
	return 0;
}

运行结果:

C++ vector模拟实现_第7张图片

 谢谢观看,看到这里希望大家给个一键三连支持~

                                C++ vector模拟实现_第8张图片 

你可能感兴趣的:(C++,博客,c++,数据结构,算法,链表)