vector底层实现及深层次拷贝问题

目录

大框架

接口实现

深层次拷贝问题(两次深拷贝)


大框架

为了与库里实现的更接近一些,先来看一下STL中是如何实现vector的(这里不是PJ版,PJ版稍微晦涩一点不容易理解,这里采用Linux下g++的版本)这里仅就大体框架做个简单介绍,其中复杂的概念不做过多解释。

 首先是vector定义的模板参数T和Alloc,这是空间配置器是一种底层实现机制,使用角度不用管。

可以看到源码做了很多typedef,主要是对格式的控制和方便理解编写。其中将T* typedef成了iterator,这个要注意,后面protected 成员变量会用到。

 这里成员变量就用到了刚刚typedef的iterator,实际上就是T* start ; T* finish;  T* end_of_storage;

T就是实例化模板参数。好像与我们设想的变量不太一样,我们设想的应该是有size和capacity的,但是这里全部统一使用指针,其实只是改变了使用方式,本质还是一样的。

 假设存在size(有效数据个数),capacity(容量),那么finish也可以表示成start+size,end_of_storage也可以表示成strat+capacity,本质是一样的。

后面是各个接口的实现,后面我们自己实现的时候具体说明,先把大框架搭建起来。

namespace bc
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};
}

为了避免和库里的冲突,将我们的实现代码封在命名空间内,库里的繁杂的typedef我们省略,只保留基本的T* 的重命名和成员变量表示。

接口实现

vector.h

#pragma once

#include<iostream>
#include<vector>
#include<assert.h>

using namespace std;

namespace bc
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{ }

		//拷贝构造写法一
		//vector(const vector<T>& v)
		//	: _start(nullptr)
		//	, _finish(nullptr)
		//	, _end_of_storage(nullptr)
		//{
		//	reserve(size());
		//	for (auto& e : v) {
		//		push_back(e);
		//	}
		//}


		//拷贝构造现代写法
		template<class InputIterator>
		vector(InputIterator first, InputIterator last) 
			: _start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			while (first != last) {
				push_back(*first);
				first++;
			}
		}

		vector(vector<T>& v) 
			: _start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);
		}

		vector(size_t n, const T& val= T()) 
			: _start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			reserve(n);
			for (size_t i = 0; i < n; ++i) {
				push_back(val);
			}
		}
		//函数重载
		vector(int n, const T& val= T())
			: _start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			reserve(n);
			for (int i = 0; i < n; ++i) {
				push_back(val);
			}
		}

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

		vector<T>& operator=(vector<T> v) {
			swap(v);
			return *this;
		}

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

		iterator begin() {
			return _start;
		}
		iterator end() {
			return _finish;
		}
		const iterator begin() const {
			return _start;
		}
		const iterator end() const {
			return _finish;
		}

		bool empty()const {
			return _finish == _start;
		}

		void clear() {
			_finish = _start;
		}

		void swap(vector<T>& v) {
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}
		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 resize(size_t n, T val = T()) {
			if (n > capacity()) {
				reserve(n);
			}
			if (n > size() && n <= capacity()) {
				while (_finish < _start + n) {
					*_finish = val;
					++_finish;
				}
			}
			if (n <= size()) {
				_finish = _start + n;
			}
		}

		void reserve(size_t n) {
			if (n > capacity()) {
				size_t oldsize = size();
				T* tmp = new T[n];
				if (_start != nullptr) {
					memcpy(tmp, _start, sizeof(T) * oldsize);
					/*for (size_t i = 0; i < oldsize; ++i) {
						tmp[i] = _start[i];
					}*/
					delete[]_start;
				}
				_start = tmp;
				_finish = tmp + oldsize;
				_end_of_storage = _start + n;
			}
		}



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


		void pop_back() {
			assert(!empty());
			_finish--;
		}

		//迭代器失效
		iterator insert(iterator pos, const T& x) {
			assert(pos >= _start);
			assert(pos < _finish);
			if (_finish == _end_of_storage) {
				size_t len = pos - _start;
				size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapacity);
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos) {
				*(end + 1) = *(end);
				--end;
			}
			*pos = x;
			_finish++;
			return pos;	
		}

		iterator erase(iterator pos) {
			assert(pos >= _start);
			assert(pos < _finish);
			iterator begin = pos + 1;
			while (begin < _finish) {
				*(begin-1) = *(begin);
				++begin;
			}
			_finish--;
			return pos;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};

下面是我给出的测试用例:

vector.h

	void Test1() {
		vector<int>a;
		a.push_back(1);
		a.push_back(1);
		a.push_back(1);
		a.push_back(1);
		a.push_back(1);
		cout << a.capacity() << endl;
		for (size_t i = 0; i < a.size(); ++i)
		{
			cout << a[i] << " ";
		}
		cout << endl;

		vector<int>::iterator it = a.begin();
		while (it != a.end()) {
			cout << (*it) << " ";
			it++;
		}
		cout << endl;

		//a.resize(2);
		for (auto e : a) {
			cout << e << " ";
		}
		cout << endl;

		cout << a.empty() << endl;

		a.pop_back();

		for (auto e : a) {
			cout << e << " ";
		}
		cout << endl;
	}

	void Test2() {
		vector<int> a;
		a.push_back(1);
		a.push_back(1);
		a.push_back(1);
		a.push_back(1);
		for (auto e : a) {
			cout << e << " ";
		}
		cout << endl;

		a.insert(a.begin(), 0);

		for (auto e : a) {
			cout << e << " ";
		}
		cout << endl;

		vector<int>::iterator it = find(a.begin(), a.end(), 1);
		if (it != a.end()) {
			a.insert(it, 2);
		}
		for (auto e : a) {
			cout << e << " ";
		}
		cout << endl;

		(*it)++;
		for (auto e : a) {
			cout << e << " ";
		}
		cout << endl;
	}

	void Test3() {
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(4);
		v.push_back(5);
		vector<int>::iterator it = v.begin();
		while (it != v.end()) {
			if ((*it) % 2 == 0) {
				it = v.erase(it);
			}
			else {
				++it;
			}
		}
		for (auto e : v) {
			cout << e << " ";
		}
		cout << endl; 
	}

	void Test4() {
		vector<int> v;
		v.push_back(1);
		v.push_back(1);
		v.push_back(1);
		v.push_back(1);
		vector<int> a(v);
		for (auto e : a) {
			cout << e << " ";
		}
		cout << endl;

		vector<int> b;
		b = a;
		for (auto e : b) {
			cout << e << " ";
		}
		cout << endl;

		vector<int>c(10, 1);
		for (auto e : c) {
			cout << e << " ";
		}
		cout << endl;
	}

void Test5()
	{
		vector<vector<int>> vv;
		vector<int> v(4, 1);
		vv.push_back(v);
		vv.push_back(v);
		vv.push_back(v);
		vv.push_back(v);
		vv.push_back(v);

		for (size_t i = 0; i < vv.size(); ++i)
		{
			for (size_t j = 0; j < vv[i].size(); ++j)
			{
				cout << vv[i][j] << " ";
			}
			cout << endl;
		}
		cout << endl;
	}	
}

test.cpp

#include"vector.h"

int main()
{
	bc::Test1();
    bc::Test2();
	bc::Test3();
	bc::Test4();
	bc::Test5();

	/*vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	std::vector<int>::iterator it = v.begin();
	while (it != v.end()) {
		if ((*it) % 2 == 0) {
			v.erase(it);
		}
		++it;
	}
	for (auto e : v) {
		cout << e << " ";
	}
	cout << endl;*/

	return 0;
}

以上是常用接口的大致实现,基本功能包含在内,还有些不太常用的接口没有实现。

深层次拷贝问题(两次深拷贝)

在测试vector<vector<int>>二维数组的时候,发现一个奇怪的现象:

当创建好vector<vector<int>> vv模型,并创建出内置数组vector<int> v (4,1)后,此时想要设定行数,也就是通过push_back插入行实现时,如果插入4个以内,没有问题,超过4个就会报错。

 

 

由此我们可以判定问题出在扩容上,具体我画图来解释。

 对于vector<vector<int>>模型要有一定理解,外部的大类型是vector<>,< >里面的类型是vector<int>,所以先从内部开始创建,vector<int> v(4,1) 构造一个{1,1,1,1}的一维数组,里面构建好了,在外面通过push_back插入,相当于插入一个一个这样的一维数组,这样就构建了二维数组,push_back多少次就相当于有多少行,里面4相当于列。

vector<int> v 数组里面的元素是int类型的,外面通过_start 指针指向v数组,指针是int* 类型的,一个指针指向一个v数组,外面还有一个_start指针指向整个vector<vector<int>>,指针类型是vector<int>*

建好二维数组后来看扩容问题,当调用reserve时,需要开辟新空间tmp,用memcpy将数据传给tmp 。                   

 而memcpy是按内存将数据拷贝给tmp的,这里的拷贝是浅拷贝,这就会导致一个问题,tmp最后也会指向_start 空间,

然后memcpy结束就是delete[]_start,delete会调用析构函数将_start空间释放,这下tmp指向的空间get不到了,tmp就是野指针了。

所以要想解决这个问题,就要解决内层浅拷贝的问题,将其改为深拷贝,这样外层tmp对象深拷贝,里层tmp数据也进行深拷贝,就不会出问题了。

解决方案:1、不用memcpy,利用operator = 重载函数将数据一个一个拷贝给tmp

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t oldSize = size();
				T* tmp = new T[n];

				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T)*oldSize);
					for (size_t i = 0; i < oldSize; ++i)
					{
						tmp[i] = _start[i];
					}

					delete[] _start;
				}
				_start = tmp;
				_finish = tmp + oldSize;
				_endofstorage = _start + n;
			}
		}

tmp[i] = _start[i] 将_start[i] 一个一个拷贝给tmp[i],这里调用operator= 是深拷贝。

		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}

但是这种方法缺点是效率太低。在C++11 中会有更好的方式解决上述问题。

以上就是深层拷贝的内容,关于底层实现还有其他不同版本,但因为复杂度等原因我们是按Linux g++使用的库实现的,感谢浏览!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值