初级C++:vector的迷你实现

框架和编译冷知识

  • 看别人代码,两成核心,熟悉框架
  • 写一项测试一下。
  • 头文件会在源文件里面展开,调用函数时只会向上去全局空间找 不会去命名空间找,不会去类域里面找。
    • 某个头文件使用了cin,则应该将cin在它上面展开。
  • 分区域断点调试
    在这里插入图片描述
    在这里插入图片描述

vector

底层还是顺序表

构造–>尾插 -->开n个空间reserve -->支持对象【】遍历数据------>支持迭代器遍历----->拷贝构造 -->赋值重载—>resize---->支持用迭代器区间构造---->----->任意位置的插入和删除。

  • 写单个函数时,思考要用到哪些成员变量或者已知条件,哪些成员变量要改变,又怎样的改变;和参数之间的成员变量又怎样的交互;是否可以借助第三方完成等等

构造,尾插,reserve、【】遍历、迭代器

基建、无参构造

template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		vector()
			:_start(nullptr)
			, _finsh(nullptr)
			, _endofstorage(nullptr)
		{}
	private:
		iterator _start;
		iterator _finsh;
		iterator _endofstorage;
	};

析构

		~vector()
        {
            //释放空间以及置空
            if (_start)
            {
                delete[] _start;
            }
            _start = _finish = _endOfStorage = nullptr;
        }

个数、容量、判空


//计算数组元素个数
size_t size()const
{
	return _finsh - _start;
}
		
//计算数组容量大小
size_t cap()const
		{
			return _endofstorage - _start;
		}
//数组判空
bool empty()
		{
			return size() == 0;
		}

push_back


		void push_back(const T& x)
		{
			if (_finsh == _endofstorage)
			{
				size_t newcap = cap() == 0 ? 5 : cap() * 2;
				reserve(newcap);
			}
			*_finsh = x;
			++_finsh;
			//insert(end(), x);
		}

reserve

		void reserve(size_t n)
        {
            if (n > capacity())
            {
                //扩容会重新开辟空间,导致成员变量为nullptr,这里记录下必要数据
                size_t sz = size();
                iterator tmp = new T[n];
                if (_start)
                {
                    for (size_t i = 0; i < sz; i++)
                    {
                        //对于内置类型进行赋值,对于自定义类型,调用其复制重载函数(如string)
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }
                _start = tmp;
                _finish = _start + sz;
                _endOfStorage = _start + n;
            }
        }

Operator[]


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

迭代器

  • 迭代器,对指针的重命名;或对指针的封装
		iterator begin()		{return _start;}
		const_iterator begin()const { return _start; }
		iterator end()      { return _finsh; }
		const_iterator end()const { return _finsh; }

拷贝构造、赋值重载的多种写法

拷贝构造1.0

---//v1(v2)
		vector(const vector<T>& type)
		{
		/v1开一个和v2一样大空间
			_start = new T[type.cap()];
			/把v2空间的数据拷贝给v1
			memcpy(_start, type._start, sizeof(T) * type.size());
			/更新v1 关于有效数据个数的地址
			_finsh = _start + type.size();
			/更新V1的容量的地址
			_endofstorage = _start + type.cap();
		}

拷贝构造2.0

  • v1(v2)用v2初始化v1,v1刚创建时。

		vector(const vector<T>& v)
			:_start(nullptr)
			, _finsh(nullptr)
			, _endofstorage(nullptr) /先初始化我自己
		{
			reserve(v.cap());   /开一个和v2一样大的空间,有空间就可以搞事情了
			for (const auto& e : v)   /对v2数组每个元素的引用尾插到我这个数组里
			{
				push_back(e);
			}
		}

赋值重载 =

  • v1=v2 , v2实参传给形参tp,形参是实参的一份临时拷贝(重开空间存数据),所以tp是v1的深拷贝
		vector<T>& operator=(vector<T> tp)
		{
			swap(tp);
			return *this;
		}
		
	void swap( vector<T>& type)
		{
			std::swap(_start, type._start);
			std::swap(_finsh, type._finsh);
			std::swap(_endofstorage, type._endofstorage);
		}


resize、迭代器区间构造

resize

void resize(size_t n, const T& value = T()) /给缺省初始化
		{
			if (n<=size())
			{
				_finsh = _start + n; /反正后面的你也访问不到
			}
			else
			{
				if (n > cap())
				{
					reserve(n);
				}
				while (_finsh<_start+n)
				{
					*_finsh = value;
					++_finsh;
				}
			}

		}

迭代器区间构造

  • 模板类中可以存在模板函数。。
		template<class Inputiterator> 
		vector(Inputiterator first, Inputiterator last)
			:_start(nullptr)
			, _finsh(nullptr)
			, _endofstorage(nullptr)
		{
			while (first!=last)
			{
				push_back(*first);
				++first;
			}
		}

插入、删除

插入

		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= begin() && pos <= end());
			
			if (_finsh == _endofstorage)
			{
				size_t len = pos - _start;	/记录pos的相对位置
				size_t newcap = cap() == 0 ? 5 : cap() * 2;
				reserve(newcap);
				pos = begin() + len;
			}
			vector<T>::iterator it = end();
			while (it>=pos)
			{
				*it = *(it - 1);
				--it;
			}
			*pos = val;
			_finsh++;
			return pos;		/返回新位置的迭代器
		}

删除

		iterator earses(iterator pos)   //删除一个,删除多个就是resize了
		{
			//返回指向删除数据的下一个数据位置
			assert(pos >= begin() && pos <= end());
			auto it = pos;
			while (it!=_finsh)
			{
				*it = *(it + 1);
				++it;
			}
			--_finsh;
			return pos;
		}

迭代器失效

find某个值,返回该值的迭代器,然后进行插入,如果遇到增容,则该迭代器原空间可能会被销毁,再用迭代器可能造成野指针访问,
或者原空间没有销毁,但指向空间的值改变了。
在这里插入图片描述


Bug修正:reserve时memcpy深度的浅拷贝问题

		void reserve(size_t n)
		{
			if (n>cap())
			{
				size_t sz = size(); /记录原finsh相对于start的距离
				T* tmp = new T[n];
				if(_start)
				{
					memcpy(tmp, _start,sizeof(T)*sz); 
					delete{} _start;  
		//假设这里T是string 对象 ,清理时,则会先调用string对象的析构,再释放_start					
				}
				_start = tmp;
				_finsh = _start + sz;
				_endofstorage = _start + n;
			}
		}

int main()
{
	vector<string> v1;
	reserve(2);
	v1.push_back("11111");
	v1.push_back("11111");
	v1.push_back("11111");//扩容 memcpy 后释放 已经清理了string 的_str
	return 0;
	
}//v 走到这里时,会再次调用string 的析构_str ,释放了两次

       -------------修正-------------
       
		void reserve(size_t n)
		{
			if (n>cap())
			{
				size_t sz = size(); //记录原finsh相对于start的距离
				T* tmp = new T[n];
				if(_start)
				{
					for(size_t i=0;i<size();++i)
					{
						tmp[i]=_start[i]
						//如果是string 对象,则是调用对象的深拷贝,
						delete[] _start; 
					}				
				}
				_start = tmp;
				_finsh = _start + sz;
				_endofstorage = _start + n;
			}
		}

结语

请添加图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值