C++中vector的实现

1. vector的原理

vector与string一样,都是在堆上开辟空间,用数组下标去访问,vector能够存储的数据的类型不像string那样只有char类型,vector在有模板的支持下,像是一个万能容器,不仅能存储默认类型(int , char ....),也能存储自定义类型。由于有下标的帮助,vector的迭代器不需要像list那样包含成员函数,他只需要一个T*(list中的Node*)。

2.vector的结构

2.1 vector的成员变量有3个

_start_finishend_of_storage)他们的类型都是迭代器(iterator),也就是T*,指向某个地址。

_start是数组首元素地址。

_finish是末尾元素下一个位置的地址(还没有存储元素)。

end_of_storage是指向该空间的最后一个能存储数据的子空间的下一个空间(从end_od_storage开始的空间不再属于可用的空间)。

2.2 size()和capacity()

_start+1就是加了sizeof(T)个字节(如果T=int,就加了4 个字节),_finish-_start=size,

获得元素的个数,同理end_of_storage-_start=capacity,获得容量的大小。

3. vector的构造函数

namespace GuYu
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		//构造函数
		vector()
		{
			_start = _finish = end_of_storage = nullptr;
		}

		vector(size_t n=0,const T& val=T())  // T() 相当于一个T类型的空字符
		{
			_start = new T[n];
			_finish = end_of_storage = _start + n;  //此时_finish和_end_of_storage指向的空间出了开辟空间的范围
			iterator it = _start;
			while (it != _finish)
			{
				*it = val;
				it++;
			}
		}

		//拷贝构造函数
		vector(const vector& v)
		{
			size_t len = v.size();
			_start = new T[len];
			_finish = end_of_storage = _start + len;
			for (int i = 0; i < len; i++)
			{
				_start[i] = v._start[i];
			}
		}

		vector(const iterator& first, const iterator& last)   // [first,last)  不包括last元素
		{
			assert(first <= last);
			size_t len = last - first;
			_start = new T[len];
			_finish = end_of_storage = _start + len;
			for (int i = 0; i < len; i++)
			{
				_start[i] = first[i];     // first[i] 等同于 *(first+i)
			}
		}

		iterator begin()
		{
			return this->_start;
		}

		iterator end()
		{
			return this->_finish;
		}

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

		void print()
		{
			size_t len = this->size();
			for (int i = 0; i < len; i++)
			{
				cout << this->_start[i] << " ";
			}
			cout << endl;
		}




	private:
		iterator _start;
		iterator _finish;
		iterator end_of_storage;
	};
}

4. resize函数实现扩容

void resize(size_t n)
{
	int len = size();
    iterator tmp = new T[n];
	int min = n > len ? len : n;     //  找到小的值进行遍历
	// 将this->_start的数据拷贝到tmp
	for (int i = 0; i < min; i++)
	{
		tmp[i] = _start[i];
	}
	std::swap(tmp, _start);   //  交换变量指向的空间
	_finish = _start + min;
	end_of_storage = _start + n;
	if (n > len)
	{
		iterator it = _finish;
		while (it != end_of_storage)
		{
			*it = val;
			it++;
		}
	}
    delete[]tmp;
	tmp = nullptr;  // 由于tmp的类型不是该类(vector),没有析构函数,需要手动销毁。

}

5. reserve函数实现扩容

void reserve()
{
	assert(size() >= capacity());
		size_t len = this->size();
		size_t capacity = this->capacity();
		capacity = capacity == 0 ? 2 : capacity * 2;
		iterator tem = new T[capacity];
		if (_start)
		{
			memcpy(tem, _start, sizeof(T) * size());   //void *memcpy(void *str1, const void *str2, size_t n) // n是拷贝的字节数
		}
		delete[]_start;
		_start = _finish = end_of_storage = nullptr;
		_start = tem;
		_finish = _start + len;
		end_of_storage = _start + capacity;
}

注意:在这里memcpy进行了浅拷贝,具有很大的危害性,在vector<string>的时候将会说明。

如下进行深拷贝

void reserve()
{
	//assert(size() >= capacity());
		size_t len = this->size();
		size_t capacity = this->capacity();
		capacity = capacity == 0 ? 2 : capacity * 2;
		iterator tem = new T[capacity];
		if (_start)
		{
			for (int i = 0; i < len; i++)
			{
				tem[i] = _start[i];
			}
		}
		delete[]_start;
		_start = _finish = end_of_storage = nullptr;
		_start = tem;
		_finish = _start + len;
		end_of_storage = _start + capacity;
}

6. push_back函数尾插

	void push_back(T val)
	{
		size_t len = size();
		if (len >= capacity())
		{
			resize(capacity()*2,0); //   每次容量不足就加倍
		}
		*_finish = val;
		_finish++;
	}

7. insert函数

iterator insert(const iterator pos, size_t n, const T val)
{
	assert(pos >= begin() && pos <= end());
	size_t numpos = pos - _start;     //记录pos的位置
	if (size() + n >= capacity())
	{
		resize(size() + n,0);  // 增容之后迭代器pos失效
	}
	//将元素向后推的思想
	iterator it1 = _start + numpos;   // 重新获取pos的位置
	iterator it = end() - 1;
	while (it >= it1)
	{
		*(it + n) = *it;
		it--;
	}
	// 将n个val拷贝到[pos,pos+n)的位置
	for (int i = 0; i < n; i++)
	{
		*(it1 + i) = val;
	}
	_finish = _finish + n;
	return pos;      //  返回的是插入的首个数据的迭代器
}
iterator insert(const iterator pos, const iterator first, const iterator last)  //  拷贝[first,last)
{
	assert(pos >= begin() && pos <= end());
	size_t n = last - first;
	size_t numpos = pos - _start;     //记录pos的位置
	if (size() + n >= capacity())
	{
		resize(size() + n,0);  // 增容之后迭代器pos失效
	}
	//将元素向后推的思想
	iterator it1 = _start + numpos;   // 重新获取pos的位置
	iterator it = end() - 1;
	while (it >= it1)
	{
		*(it + n) = *it;
		it--;
	}
	// 将n个val拷贝到[pos,pos+n)的位置
	for (int i = 0; i < n; i++)
	{
		*(it1 + i) = *(first + i);
	}
	_finish = _finish + n;
	return pos;      //  返回的是插入的首个数据的迭代器
}

8. erase函数

iterator erase(const iterator pos)
{
	assert(pos >= _start && pos <= _finish);
	int i = 0;
	while (pos + i + 1< _finish) // 往前推
	{
		*(pos + i) = *(pos + i + 1);
		i++;
	}
	_finish--;
	return pos;  //  返回的是删除迭代器的下一个元素对应的迭代器
}

9.operator[]函数

T& operator[](size_t pos)
{
	assert(pos >= 0, pos <= this->size());
	return _start[pos];
}

10. 对vector<string>类型的理解

memcpy拷贝的的是vector内部的字节,再拷贝一个string类型数据的时候,该string内部包含的 _str  _size  _capacity以地址的形式&(char*_str,size_t  _size,size_t _capacity)存放在vector中 作为他的成员,拷贝vector的字节,就是将地址拷贝了过去(tmp中string指向的还是原来(_start)的空间),当delete[]vector的时候, 会先调用string的析构函数,将char*_str置空(原来的空间被释放),而&(char*_str)已经传给了tem,tmp中的str就变成了野指针,导致访问冲突。

当深拷贝的时候,用tmp[i]=_start[i](调用string里面的operator=重载操作符),会给tmp[i]新开空间,再将_start[i]的字符串拷贝过来(等同于string a="abcdef"),此时就会调用string类的构造函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值