vector的模拟实现和memcpy的深浅拷贝问题

vector的模拟实现

要实现的接口有:构造和拷贝构造函数、析构、赋值运算符的重载、begin()、end()、reserve()、push_back()、pop_back()、insert()、erase()、operator[]、resize。

首先要定义的是vector中的成员变量,这里我们使用迭代器去定义,这里可以方便后续的操作,且vector的迭代器底层实现就是指针,更加方便我们去使用,下面是成员变量,以及接下来遍历需要用到的一些接口

    template<class T>	
    class vector
    {
    public:
        typedef T* iterator;
		typedef const T* const_iterator;
		size_t size()const
		{
			return _finish - _start;
		}
		size_t capacity()const
		{
			return _endofstorage - _start;
		}
		T& operator[](size_t n)
		{
			assert(n < size());
			return _start[n];
		}
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin()const
		{
			return _start;
		}
		const_iterator end()const
		{
			return _finish;
		}
    private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};

定义完成员变量就要来遍历成员函数了我的习惯是先定义四个默认成员函数

    class vector
    {
    public:
		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{}
		~vector()
		{
			delete[] _start;
			_start = _endofstorage = _finish = nullptr;
		}
		//传统写法
		//vector(const vector<T>& x)
		//{
		//	reserve(x.capacity());
		//	for (size_t i = 0; i < x.size(); i++)
		//	{
		//		_start[i] = x._start[i];
		//	}
		//	_finish = _start + x.size();
		//	_endofstorage = _start + x.capacity();
		//}
		vector(const vector<T>& x)
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{
			reserve(x.capacity());
			for (const auto& ch : x)
				push_back(ch);
		}
		//vector<int>& operator=(const vector <T>& x)
		//{
		//	if (this != &x)
		//	{
		//		resize(x.capacity());
		//		for (size_t i = 0; i < x.size(); i++)
		//		{
		//			_start[i] = x._start[i];
		//		}
		//		_finish = _start + x.size();
		//		_endofstorage = _start + x.capacity();
		//	}
		//	return *this;
		//}
		void swap(vector<T>& e1)//这里一定是引用
		{
			::swap(e1._endofstorage, _endofstorage);
			::swap(e1._finish, _finish);
			::swap(e1._start, _start);
		}
		vector<int>& operator=(vector <T> x)
		{
			if(this != &x)
				swap(x);
			return *this;
		}
};

这里我对赋值运算符和拷贝构造写了两种写法,一种是传统的写法,另一种是现代的写法(更加简洁)接下来我们实现insert()和erase()

		void insert(iterator pos, const T& x)//一定要小心迭代器失效的问题
		{
			assert(pos <= _finish);
			if (_finish == _endofstorage)//增容
			{
				size_t n = pos - _start;
				int newendofstorage = capacity() == 0 ? 2 : 2 * capacity();
				reserve(newendofstorage);
				pos = _start + n;//更新迭代器
			}
			for (iterator tmp = _finish; tmp >pos; tmp--)
			{
				*tmp = *(tmp - 1);
			}
			*pos = x;
			_finish++;
		}
		iterator& erase(iterator pos)
		{
			assert(pos < _finish);
			for (iterator tmp = pos; tmp < _finish-1; tmp++)
			{
				*tmp = *(tmp + 1);
			}
			_finish--;
			return this->_start;
		}

在insert中有一个很重要的函数还没有写,那就是reserve和resize

void resize(const size_t& n, const T& x = T()/*可以理解为去调内置类型的构造函数或者自定义类型的构造函数*/)
{
	if (n <= size())
    {
		_finish  = _start + n;
	}
	else
	{
		if (n > capacity())
		{
			reserve(n);
		}
		iterator tmp = _finish;
		while (tmp != _start + n)
		{
			*tmp = x;
			tmp++;
		}
		_finish = _start + n;
    }
}
void reserve(size_t newcapacity)
{
	if (newcapacity > capacity())
	{
		size_t len = size();
		iterator newspace = new T[newcapacity];
		if (_start)
		{
			//memcpy(newspace, _start, sizeof(T) * size());//memcpy是根据字节来拷贝的浅拷贝
			for (size_t i = 0; i < size(); i++)
			newspace[i] = _start[i];//这里是通过赋值调T的operator= 来进行深拷贝
			delete[] _start;
		}
			_start = newspace;
			_finish = newspace + len;
			_endofstorage = newspace + newcapacity;

	}
}

我们对写了insert和erase接下来写push_back和pop_back就可以进行复用

void push_back(const T& x)
{
	//if (_finish == _endofstorage)
	//{	
	//	int newendofstorage = capacity() == 0 ? 2 : 2 * capacity();
	//	reserve(newendofstorage);
	//}
	//*_finish = x;
	//++_finish;
	insert(end(), x);
}
void pop_back()
{
	assert(_finish >= _start);
	//_finish--;
	this->erase(_finish - 1);
}

以上就是对vector的模拟实现的全部内容了,很多需要注意的细节都以注释的形式标注好了,当然还有很多接口没有实现,之后我们在进行讨论。

memcpy的深浅拷贝问题

使用mem系列的函数时一定要注意,mem系列的函数都是按照字节来进行拷贝的,例如我们使用memset进行初始化时,初始化为零倒是不会出现问题,但是想要初始化为一,恐怕就没有那么容易了,他会按照字节在每一个字节上面赋值为一,也就是十六进制的1111.

memcpy同理,他是按照字节去拷贝的在上面的reserve函数中如果使用memset去拷贝对于vector中存放的内置类型影响不大,但是当vector中存放的是自定义类型时,结果就不一样了,我们用test_vector5()来进行举例

	void test_vector5()
	{
		vector<string> v;
		v.push_back("111");
		v.push_back("222");
		v.push_back("333");
		v.push_back("444");
		for (const auto& ch : v)
		{
			cout << ch << " ";
		}
		cout << endl;
	}

当代码为以上时我们进入调试阶段

 他不影响正常的打印以及存放,当这个数据很长时,例如

	void test_vector5()
	{
		vector<string> v;
		v.push_back("1111111111111111111111111");
		v.push_back("2222222222222222222222222");
		v.push_back("3333333333333333333333333");
		v.push_back("4444444444444444444444444");
		for (const auto& ch : v)
		{
			cout << ch << " ";
		}
		cout << endl;
	}

这一段代码再次运行他就会报错,并且前面插入的字符串变成随机数

这里就是因为memcpy的浅拷贝问题,他只是按照字节一个一个拷贝,他并没有拷贝之前的数组

只是拷贝了一个指针指向了原数组空间 当释放了前面一个空间时,前面那一块空间就不属于他了。且已经析构过一次,再次析构就会报错,

为了避免这种问题我们上面的代码使用了赋值的方式来进行深拷贝

void reserve(size_t newcapacity)
{
	if (newcapacity > capacity())
	{
		size_t len = size();
		iterator newspace = new T[newcapacity];
		if (_start)
		{
			//memcpy(newspace, _start, sizeof(T) * size());//memcpy是根据字节来拷贝的浅拷贝
			for (size_t i = 0; i < size(); i++)
			newspace[i] = _start[i];//这里是通过赋值调T的operator= 来进行深拷贝
			delete[] _start;
		}
			_start = newspace;
			_finish = newspace + len;
			_endofstorage = newspace + newcapacity;

	}
}

 

  • 14
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值