目录
一、vector.h
二、填充构造函数的重载
三、拷贝构造函数
四、迭代器失效问题
一、vector.h
#pragma once
#include
#include
namespace yzz
{
template
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
/*-------- 构造函数和析构函数 --------*/
// 默认构造函数
vector()
: _start(0), _finish(0), _end_of_storage(0)
{ }
// 填充构造函数
vector(size_t n, const T& val = T())
: _start(new T[n]), _finish(_start + n), _end_of_storage(_finish)
{
for (size_t i = 0; i < n; ++i)
{
_start[i] = val;
}
}
// 填充构造函数的重载
vector(int n, const T& val = T())
: _start(new T[n]), _finish(_start + n), _end_of_storage(_finish)
{
for (size_t i = 0; i < n; ++i)
{
_start[i] = val;
}
}
// 范围构造函数
template
vector(InputIterator first, InputIterator last)
: _start(0), _finish(0), _end_of_storage(0)
{
while (first != last)
{
push_back(*first);
++first;
}
}
// 拷贝构造函数(实现深拷贝)
vector(const vector& v)
: _start(new T[v.capacity()]),
_finish(_start + v.size()),
_end_of_storage(_start + v.capacity())
{
for (size_t i = 0; i < v.size(); ++i)
{
_start[i] = v._start[i];
}
}
// 析构函数
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _end_of_storage = 0;
}
}
/*-------- 赋值运算符重载(实现深拷贝) --------*/
// 利用上面写好的拷贝构造函数实现深拷贝
void swap(vector& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
vector& operator=(vector tmp)
{
swap(tmp);
return *this;
}
/*-------- 容量操作 --------*/
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _end_of_storage - _start;
}
bool empty()
{
return _start == _finish;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
// 注意:此处与拷贝构造函数一样,不能使用 memcpy
for (size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
// 异地扩容,因此需要更新 _finish 和 _end_of_storage
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish != _start + n)
{
*_finish = val;
++_finish;
}
}
}
/*-------- 遍历及访问操作 --------*/
T& operator[](size_t i)
{
assert(i < size()); // 前提是下标 i 合法
return _start[i];
}
const T& operator[](size_t i) const
{
assert(i < size()); // 前提是下标 i 合法
return _start[i];
}
iterator begin()
{
return _start;
}
const_iterator begin() const
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator end() const
{
return _finish;
}
/*-------- 修改操作 --------*/
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start && pos <= _finish); // 前提是 pos 合法
if (_finish == _end_of_storage) // 考虑是否需要扩容
{
// 注意:因为是异地扩容,所以内部迭代器 pos 就会失效
size_t sz = size();
size_t old_capacity = capacity();
reserve(old_capacity == 0 ? 1 : 2 * old_capacity); // 2 倍扩容
// 更新 pos
pos = _start + sz;
}
for (iterator end = _finish - 1; end >= pos; --end)
{
*(end + 1) = *end;
}
*pos = val;
++_finish;
// 通过返回更新的 pos 来解决外部迭代器失效的问题。
// 注意:不能通过 iterator& pos 的方法来解决外部迭代器失效的问题,
// 因为对于类似于 v.insert(v.begin(), 100); 的语句,形参 pos 接收的是一个临时变量,
// 临时变量具有常性,必须用常引用接收。
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start && pos < _finish); // 前提是 pos 合法
for (iterator it = pos + 1; it < _finish; ++it)
{
*(it - 1) = *it;
}
--_finish;
// 假设是尾删操作,那么此时 pos == _finish,即内外部的迭代器都失效了
return pos;
}
void push_back(const T& val)
{
//法一:
//if (_finish == _end_of_storage) // 首先考虑是否需要扩容
//{
// size_t old_capacity = capacity();
// reserve(old_capacity == 0 ? 5 : 2 * old_capacity);
//}
//*_finish = val;
//++_finish;
//法二(复用):
insert(end(), val);
}
void pop_back()
{
//法一:
//assert(!empty()); // 前提是容器非空
//--_finish;
//法二(复用):
erase(end() - 1);
}
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
}
表面上,提供了填充构造函数:
vector(size_t n, const T& val = T());
似乎就不再需要提供下面的重载了:
vector(int n, const T& val = T());
但实际上,对于下面这样的语句,就会出现问题,即:
yzz::vector v(5, 10);
因为编译器在编译时,T 已经被实例化为 int
了,而 5 和 10 被编译器默认为 int
类型,所以如果不提供上面的填充构造函数的重载,编译器就会选择范围构造函数,而非填充构造函数,即:
template
vector(InputIterator first, InputIterator last);
而当 InputIterator
被实例化为 int
,当进行解引用操作时,就会出现错误。
拷贝构造函数的具体实现如下:
vector(const vector& v)
: _start(new T[v.capacity()]),
_finish(_start + v.size()),
_end_of_storage(_start + v.capacity())
{
for (size_t i = 0; i < v.size(); ++i)
{
_start[i] = v._start[i];
}
}
注意:在拷贝构造函数的实现中,不能将 for 语句更改为下面的语句:
memcpy(_start, v._start, sizeof(T) * v.size());
假设 T 被实例化为具有指针成员变量的类,例如 string 类,使用 memcpy
就会导致 vector 中的 string 类对象是通过浅拷贝,而非深拷贝构造的,与此同时会发生内存泄漏的问题。例如:
yzz::vector v1;
v1.push_back("11111");
v1.push_back("22222");
v1.push_back("33333");
v1.push_back("44444");
v1.push_back("55555");
yzz::vector v2(v1);
而通过 T 类中已实现深拷贝的赋值运算符重载就可以解决以上问题。
在 insert 及 erase 的实现中,涉及到了内外部迭代器失效的问题,而解决外部迭代器失效的办法就是在使用外部迭代器之前,通过返回值对迭代器重新赋值即可。
例如,删除 vector 中所有的偶数:
#include
#include
using namespace std;
int main()
{
vector v{ 1, 2, 2, 2, 3, 4, 5, 6 };
vector::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
it = v.erase(it); // 不能直接写 v.erase(it); 因为在 vs 中会直接报错
else
++it;
}
for (auto e : v)
{
cout << e << " ";
}
// 1 3 5
cout << endl;
return 0;
}