目录
库中定义的vector类模板在分配空间的时候使用的是内存池,在这里我们使用的是new来模拟实现。
vector容器可以替代掉string容器吗?
不可以!因为string是专门用来处理字符串的 ,但是vector不是,再有在操作字符串时string会默认自动在结尾加‘\0’用来表示字符串的结尾,但是vector不会,这样可以更好的去兼容c语言,string有很多专用接口,vector不支持。
一.vector类对象的实例化
当我们看到一个类对象的时候首先应该想到一副图。
我们对vector对象的操作都是在对这个线性表的操作。
类成员变量和构造函数和析构函数:
template <class T>
class vector
{
public:
typedef T value_type;
typedef value_type* iterator;
typedef const value_type* const_iterator;
//成员函数
//默认成员函数
//构造函数
vector()
: start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{}
//析构函数
~vector()
{
start = nullptr;
finish = nullptr;
end_of_storage = nullptr;
}
private:
//成员变量
iterator start;
iterator finish;
iterator end_of_storage;
};
在类模板中还可以定义函数模板。
二.向数组中插入数据
void reserve(size_t rsize)
{
if (rsize> capacity())
{
//保存原数组中的数据量
size_t nums = size();
//开辟新的空间
iterator tmp = new value_type[rsize + 1];
if (start)
{
//将原数组中的数据拷贝到新申请的空间中去
//但是此时会有一个问题就是如果vector模板实例化的类型是内置类型,那么没有问题,
//但是如果是自定义类型那么就会产生深浅拷贝的问题。
//memcpy(tmp, start, sizeof(value_type) * nums);
//解决办法
int i_i = 0;
for (i_i = 0; i_i < nums; i_i++)
{
tmp[i_i] = start[i_i];
}
//释放原空间
delete[] start;
}
//更改参数
start = tmp;
finish = start + nums;
end_of_storage = start + rsize;
}
}
这里拷贝有一个问题:
如果vector顺序表中的每一个元素是内置类型那么没问题,使用memcpy进行浅拷贝不会产生任何问题。
但是:
像这样使用自定义类型就会出现问题了
会产生这样的问题,此时我们在释放原空间,那么不就相当于产生了野指针的情况吗。
解决方法
通过调用自定义类型的赋值重载来解决这个问题!
三.实现vector数据的遍历
//实现迭代器
iterator begin()
{
return start;
}
iterator end()
{
return finish;
}
const_iterator begin()const
{
return start;
}
const_iterator end()const
{
return finish;
}
//实现v[]
value_type operator[](size_t index)
{
assert(index > 0 && index <= size());
return *(start + index - 1);
}
//实现返回当前数组的最大存储空间的大小
size_t capacity()
{
return end_of_storage - start;
}
//实现返回当前数组的数据量
size_t size()
{
return finish - start;
}
四.insert和erase产生的迭代器失效的问题
insert方法中内部产生的迭代器失效问题:因为空间不足要扩容,而保存迭代器的变量中还保存原空间的地址,这样就造成了迭代器的失效。
而外部迭代器的失效的问题是通过返回迭代器的方式来解决的。
//实现insert和erase
iterator insert(iterator pos, const value_type& data)
{
//检查插入位置是否正确
assert(pos >= start && pos < finish);
//判断存储空间是否足够
if (finish == end_of_storage)
{
//记录数据位置,防止发生迭代器失效的问题
size_t length = pos - start;
//开辟空间
reserve(capacity() == 0 ? 4 : capacity() * 2);
//获取在新开辟空间的位置
pos = start + length;
}
//挪动数据
iterator end = finish - 1;
while (end >= pos)
{
*(end + 1) = *(end);
end--;
}
//放入数据
*pos = data;
finish++;
//防止迭代器失效,返回此时的pos
return pos;
}
iterator erase(iterator pos)
{
//判断数据是否正确
assert(pos >= start && pos < finish);
//挪动删除数据
iterator tmp = pos;
while (tmp < (finish - 1))
{
*(tmp) = *(tmp + 1);
tmp++;
}
finish--;
//防止迭代器失效
return pos;
}
erase这里虽然不会产生上述insert的问题,但是因为不同环境的检查迭代器失效的方式不同,所以在erase后也会产生迭代器失效的问题。通过返回迭代器的方式解决问题。
四.resize
对于resize的第二个参数:因为resize方法是调整一段空间,并且如果扩容会对新空间进行初始化,所以不同的类型的顺序表的初始化不同,所以有
这里value_type()是一个匿名对象,匿名对象具有常性,所以要使用const引用,且要保证具有默认构造函数,那内置内信哪里来的默认构造函数,这里注意C++在有了模板后对内置类型进行了升级,内置类型也有了默认构造函数,
五.拷贝构造和赋值运算符的重
//拷贝构造
vector(const vector<T>& v)
: start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{
reserve(v.capacity());
for (auto e : v)
{
push_back(e);
}
}
//赋值重载
vector<T>& operator=(vector<T> v)
{
Swap(v);
return *this;
}