1. vector的原理
vector与string一样,都是在堆上开辟空间,用数组下标去访问,vector能够存储的数据的类型不像string那样只有char类型,vector在有模板的支持下,像是一个万能容器,不仅能存储默认类型(int , char ....),也能存储自定义类型。由于有下标的帮助,vector的迭代器不需要像list那样包含成员函数,他只需要一个T*(list中的Node*)。
2.vector的结构
2.1 vector的成员变量有3个
(_start,_finish,end_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类的构造函数。