一、vector的注意事项


1. [] 和 at() 的越界访问


1
2
3
4
5
6
7
8
9
10
11
12
vector<int> v1{ 1,2,3,4 };

//v1[4] = 5; //越界直接结束,assert判断

try
{
v1.at(4) = 5; //抛异常
}
catch (...)
{
cout << "beyond" << endl; //输出beyond
}

2.迭代器失效问题


简单来说就是在迭代器创建后
使用了reverse,resize,insert,push_back等导致的增容
导致迭代器仍然指向已经被释放的旧空间

一个应对迭代器失效的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   //删除容器中所有的偶数
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
v.erase(it);
}
//然后该怎么++it?

++it; //此时会报错,为什么?
//erase删除一个数据后,其他数据向前移动,移动后其实it已经指向我们想要遍历的下一个元素
//此时再++it就会导致错过部分数据

else
{
++it; //那么这样处理可以么?
//Linux环境下这样可以达到目的,但是vs编译环境自动检查且较为严格,仍然报错
}
}


//正确做法
vector<int>::iterator it2 = v.begin();
while (it2 != v.end())
{
if (*it2 % 2 == 0)
it2 = v.erase(it2); //正确做法 erase会返回删除的it的下一个位置的迭代器
else
++it2;
}

二、vector的模拟实现


有以下要点:

reserve中浅拷贝memmove问题
新型拷贝构造和重载赋值符写法
insert时的迭代器失效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
namespace 9TSe
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}

//v2(v1)
//vector(const vector<T>& v)
//{
// _start = new T[v.capacity()]; //开辟新空间
// _finish = _start;
// _endofstorage = _start + capacity();

// for (size_t i = 0; i < v.size(); ++i)
// {
// *_finish = v[i];
// ++_finish;
// }
//}

//拷贝构造的另一种方法
vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
for (auto& e : v)
{
reserve(v.capacity());
push_back(e);
}
}

//vector<T>& operator=(const vector<T>& v)
//{
// if (*this != &v) //防止自己赋值给自己
// {
// delete[] _start; //s1 = s2 s1有自己的空间,先释放
// _start = new T[v.capacity()];

// memcpy(_start, v._start, sizeof(T) * v.size());
// }
// return *this;
//}

//重载赋值运算符简便写法
void swap(vector<T>& v)
{
::swap(_start, v._start);
::swap(_finish, v._finish);
::swap(_endofstorage, v._endofstorage);
}

vector<T>& operator=(vector<T> v)
{
if (*this != &v)
swap(v); //这里不用库内自带的swap是因为,其会生成三个深拷贝,有很大的代价
return *this;
}

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

void reserve(size_t n)
{
if (n > capacity()) //增大容量时
{
size_t sz = size();
T* tmp = new T[n];
if (_start) //这里的用意是防止_start为空指针时扩容,会导致memcpy出错(memcpy内不能存在空指针)
{
//memcpy(tmp, _start, sizeof(T) * size()); //这个memcpy是浅拷贝,当T为string时,会导致指针指向同一位置,释放两次同一空间
for (size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = tmp + sz; //如果这里赋予 tmp + size() 会出错
//sz的计算方式是_finish-_start
//_start已经指向新空间了,finish还指向旧空间
_endofstorage = tmp + n;//这里如果赋予tmp + capacity() 也会出错,与上同理
}

}

void resize(size_t n, const T& v = T()) //这里的T()为T类型的默认缺省值,有int() double() 等都有
//int() double()等内置类型,只是专门创建了此形式的东西
//string ,vector 是调用构造函数
{
if (n < size()) //缩容
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}

while (_finish < _start + n)
{
*_finish = v;
++_finish;
}
}
}

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


void insert(iterator pos, const T& v) //这里会有迭代器失效问题
{
assert(pos <= _finish);
if (_finish == _endofstorage)//pos会扩容后失效
{
size_t n = pos - _start; //因此要记录之间的距离,以明确新pos指针指向的位置

size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);

pos = _start + n; //设置新pos
}

iterator end = _finish - 1;
while (end >= pos) //移动数据
{
*(end + 1) = *end;
end--;
}
*pos = v;
++_finish;
}

iterator erase(iterator pos)
{
assert(pos < _finish&& _start != _finish);
iterator it = pos;
while (it < _finish)
{
*it = *(it + 1);
++it;
}
_finish--;
return pos;
}


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

size_t capacity()const
{
return _endofstorage - _start;
}

iterator end()
{
return _finish;
}

iterator begin()
{
return _start;
}

const_iterator end()const
{
return _finish;
}

const_iterator begin()const
{
return _start;
}

T& operator[](size_t i)
{
assert(i < size());
return *(_start + i);
}

const T& operator[](size_t i)const
{
assert(i < size());
return *(_start + i);
}

void pop_back()
{
assert(_start < _finish);
_finish--;
}

private:
iterator _start;
iterator _finish; //这个指针指向的是最后一个数据的下一个数据的位置
iterator _endofstorage;
};
}