文章目录
1.标准库中的vector类
1.1vector 简单介绍
vector是表示可变数组大小的容器,和string类有很多相似的地方,这里就不赘述了
1.2 vector的构造函数
重点掌握1234的构造方法
为了便于观察结果,首先构造一个print_vector函数
print_vector:
void print_vector(const vector<T>& v)
{
typename vector<T>::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
构造函数:
void vector_test1()
{
vector<int> v1; //方法1
v1.push_back(1);
v1.push_back(2);
print_vector(v1);
vector<int> v2(10, 0); //方法2
print_vector(v2);
string str("hello world");
vector<char> v3(str.begin(), str.end()); //方法3
print_vector(v3);
vector<int> v4(v1); //方法4
print_vector(v4);
}
allocator是内存分配器的意思,凭借现有知识还无法理解,就先跳过
方法1:调用默认构造函数,并初始化成员变量
方法2:前一个参数表示顺序表元素数量,后一个参数表示顺序表内元素初始化的值
方法3:使用迭代器,用迭代器依次插入数据
方法4:拷贝构造
1.3 vector的三种遍历方式
void vector_test2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
for (size_t i = 0; i < v.size(); i++) //1.operator[]
{
cout << v[i] << " ";
}
cout << endl;
vector<int>::iterator it = v.begin(); //迭代器
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
for (auto e : v) //范围for
{
cout << e << " ";
}
cout << endl;
}
和4.1STL-string类中的类似,就不展开了
1.4 vector的扩容方式
分别在两种编译器下设计小实验观察vector的增容方式
小测试:
```c
void vector_test2()
{
size_t sz;
vector<int> v1;
sz = v1.capacity();
cout<< "making v1 grow.. \n";
for (size_t i = 0; i < 100; i++)
{
v1.push_back(i);
if (sz != v1.capacity())
{
sz = v1.capacity();
cout << "capacity changed:" << sz << endl;
}
}
}
int main()
{
vector_test4();
return 0;
}
1.g++
[clx@VM-20-6-centos test1_C++]$ ./test
making v1 grow..
capacity changed:1
capacity changed:2
capacity changed:4
capacity changed:8
capacity changed:16
capacity changed:32
capacity changed:64
capacity changed:128
结论:在Linux操作系统g++编译器下,vector扩容遵循二倍扩容、
小结:频繁扩容会降低效率,假如刚开始就知道需要空间的大致大小,就可以进行手动扩容提高效率
2.VS
在Vs中当capacity小于五时一个一个扩容,当capacity要大于五时,1.5倍扩容
1.5 vector的增删查改
增:
删:
查:
头文件:#include < algorithm >
find函数:find函数具有三个形参,前两个是迭代器,最后一个是元素的值。find函数可以通过迭代器标记区间,对区间内的元素进行遍历查找。find(v.begin(), v.end(), 3)的意思就是查找[v.begin(),v.end())区间内第一个值为3的元素,并返回它的地址
注意:vector类并没有自己的find函数,其查找函数是使用所有容器都有的迭代器进行实现,此find函数在算法模块中,当然也有rfind()等函数
1.6 迭代器失效
在实际操作中,迭代器有两种情况会失
1.意义改变
2.野指针
测试1:pos意义改变
void vector_test1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
vector<int>::iterator pos = find(v.begin(), v.end(), 3); --找到3的位置
v.insert(pos, 30); --在3的位置插入30
for (auto e : v) --打印顺序表
{
cout << e << " ";
}
cout << endl;
v.erase(pos); --删除3
for (auto e: v)
{
cout << e << " ";
} --打印顺序表
cout << endl;
}
编译运行我们的代码
[clx@VM-20-6-centos test1_C++]$ make
make: `test' is up to date.
[clx@VM-20-6-centos test1_C++]$ ./test
1 2 30 3 4 5 6
1 2 3 4 5 6 --3并没有被删除,30被删了
应该不难理解,pos找到3的位置之后,我们又在顺序表中插入了30这个数据。30后面的数据发生了平移。而原来pos指向的空间变成了30.所以就把30删掉了
本质:pos的意义发生了改变
思考:使用insert可能发生扩容,若扩容则开辟一块新空间,为什么还可以使用pos找到对应数据呢。
若函数扩容,insert函数肯定知道,它就可以自身改变pos的值重新让其指向新开辟空间的原来位置。并且insert生成的pos仅仅是实参的拷贝,所以insert中pos改变并不影响外面pos的值
根据上述思考就可以找到第二种迭代器失效情况
测试二:pos野指针问题
void vector_test2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3); --首先先插入三个数字
vector<int>::iterator pos = find(v.begin(), v.end(), 2); --找到2的位置
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7); --再插入四个数据
v.erase(pos); --删除2
for (auto e: v) --遍历顺序表
{
cout << e << " ";
}
cout << endl;
}
运行程序
[clx@VM-20-6-centos test1_C++]$ ./test
2 3 4 5 6 7
*** Error in `./test': double free or corruption (out): 0x00000000018fec60 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x81329)[0x7fd15cbf0329]
./test[0x401f6c]
./test[0x4018ac]
./test[0x401379]
./test[0x4010c5]
./test[0x400fd0]
./test[0x400fff]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7fd15cb91555]
./test[0x400ad9]
======= Memory map: ========
00400000-00404000 r-xp 00000000 fd:01 793720 /home/clx/Lesson_Linux/lesson_7_31/test1_C++/test
00603000-00604000 r--p 00003000 fd:01 793720 /home/clx/Lesson_Linux/lesson_7_31/test1_C++/test
00604000-00605000 rw-p 00004000 fd:01 793720 /home/clx/Lesson_Linux/lesson_7_31/test1_C++/test
018ed000-0191f000 rw-p 00000000 00:00 0 [heap]
7fd158000000-7fd158021000 rw-p 00000000 00:00 0
7fd158021000-7fd15c000000 ---p 00000000 00:00 0
7fd15c96b000-7fd15c96d000 r-xp 00000000 fd:01 3494 /usr/lib64/libdl-2.17.so
7fd15c96d000-7fd15cb6d000 ---p 00002000 fd:01 3494 /usr/lib64/libdl-2.17.so
7fd15cb6d000-7fd15cb6e000 r--p 00002000 fd:01 3494 /usr/lib64/libdl-2.17.so
7fd15cb6e000-7fd15cb6f000 rw-p 00003000 fd:01 3494 /usr/lib64/libdl-2.17.so
7fd15cb6f000-7fd15cd33000 r-xp 00000000 fd:01 3486 /usr/lib64/libc-2.17.so
7fd15cd33000-7fd15cf32000 ---p 001c4000 fd:01 3486 /usr/lib64/libc-2.17.so
7fd15cf32000-7fd15cf36000 r--p 001c3000 fd:01 3486 /usr/lib64/libc-2.17.so
7fd15cf36000-7fd15cf38000 rw-p 001c7000 fd:01 3486 /usr/lib64/libc-2.17.so
7fd15cf38000-7fd15cf3d000 rw-p 00000000 00:00 0
7fd15cf3d000-7fd15cf52000 r-xp 00000000 fd:01 3501 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7fd15cf52000-7fd15d151000 ---p 00015000 fd:01 3501 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7fd15d151000-7fd15d152000 r--p 00014000 fd:01 3501 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7fd15d152000-7fd15d153000 rw-p 00015000 fd:01 3501 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7fd15d153000-7fd15d254000 r-xp 00000000 fd:01 3496 /usr/lib64/libm-2.17.so
7fd15d254000-7fd15d453000 ---p 00101000 fd:01 3496 /usr/lib64/libm-2.17.so
7fd15d453000-7fd15d454000 r--p 00100000 fd:01 3496 /usr/lib64/libm-2.17.so
7fd15d454000-7fd15d455000 rw-p 00101000 fd:01 3496 /usr/lib64/libm-2.17.so
7fd15d455000-7fd15d5c6000 r-xp 00000000 fd:01 659485 /home/clx/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6
7fd15d5c6000-7fd15d7c6000 ---p 00171000 fd:01 659485 /home/clx/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6
7fd15d7c6000-7fd15d7d0000 r--p 00171000 fd:01 659485 /home/clx/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6
7fd15d7d0000-7fd15d7d2000 rw-p 0017b000 fd:01 659485 /home/clx/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6
7fd15d7d2000-7fd15d7d6000 rw-p 00000000 00:00 0
7fd15d7d6000-7fd15d7f8000 r-xp 00000000 fd:01 3479 /usr/lib64/ld-2.17.so
7fd15d8e0000-7fd15d8e5000 rw-p 00000000 00:00 0
7fd15d8ed000-7fd15d8ef000 rw-p 00000000 00:00 0
7fd15d8ef000-7fd15d8f2000 r-xp 00000000 fd:01 16196 /usr/lib64/libonion_security.so.1.0.19
7fd15d8f2000-7fd15d9f2000 ---p 00003000 fd:01 16196 /usr/lib64/libonion_security.so.1.0.19
7fd15d9f2000-7fd15d9f3000 rw-p 00003000 fd:01 16196 /usr/lib64/libonion_security.so.1.0.19
7fd15d9f3000-7fd15d9f7000 rw-p 00000000 00:00 0
7fd15d9f7000-7fd15d9f8000 r--p 00021000 fd:01 3479 /usr/lib64/ld-2.17.so
7fd15d9f8000-7fd15d9f9000 rw-p 00022000 fd:01 3479 /usr/lib64/ld-2.17.so
7fd15d9f9000-7fd15d9fa000 rw-p 00000000 00:00 0
7fff2b7be000-7fff2b7df000 rw-p 00000000 00:00 0 [stack]
7fff2b7f0000-7fff2b7f2000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted
运行结果:内存错误
这是因为使用了野指针导致的。上面我们做过实现,Linux中g++编译器下vector采用二倍扩容机制。当我们插入三个数据后再次插入数据。顺序表进行扩容,发生深拷贝。开辟一块更大的空间将原空间数据拷贝过去,并释放原空间。此时pos指向的空间已经被释放。但pos还想访问并且删除此空间数据。产生非法访问。所以此时pos就是野指针
那么如何避免这两种问题呢
第一种:在定义完pos之后若对原数据进行挪动。重新使用find函数给pos赋值。保证新pos到pos被使用这段代码中没有将数据挪动。
第二种:合上面相近,pos定义后马上使用,不要在pos定义后进行增容扩容等。若有重新给pos赋值
为了更好的使用迭代器,STL中的函数实现包含了一些特性,帮助迭代器的使用和实现
测试三:erase的返回值
void vector_test3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6); --插入六个数据
vector<int>::iterator it = v.begin();
while (it != v.end()) --遍历数据
{
if (*it % 2 == 0) --若是二的倍数删除该数据
{
v.erase(it);
}
it++;
}
for (auto e : v) --打印数据
{
cout << e << " ";
}
cout << endl;
}
运行程序
[clx@VM-20-6-centos test1_C++]$ make
g++ -c test.cpp -std=c++11
g++ -c vector.cpp -std=c++11
g++ -o test test.o vector.o -std=c++11
[clx@VM-20-6-centos test1_C++]$ ./test
Segmentation fault
显示分段故障
进行画图分析,为什么程序会崩
通过画图我们发现
第一:3 和 5并没有被判断语句判断。erase()函数将整个函数都向前移动了一格再加上it++跳过了这两个数值。假如这两个数为偶数,则无法被筛选出来。
第二:若最后一个数为偶数,那么最后一个数被删除v.end()将会移动到it位置,it++就跳过了v.end(),那么这个循环语句就无法结束
那么如何解决这个问题呢
我们发现erase函数的返回值是一个迭代器,这个迭代器会返回erase指定删除的数据的下一个数据位置,也就是erase(pos)中的pos位置。那么我们可以通过it接收pos,这样就可以避免it跳过数据
解决方案:
void vector_test4()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
vector<int>::iterator it = v.begin();
while (it != v.end()) --增加一个else语句,删除和加加只能分开执行
{
if (*it % 2 == 0)
{
it = v.erase(it);
}
else
{
it++;
}
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
在这里看上去不接受it的返回值也可以。但是在部分编译器中如VS,可能会对it++这一行为进行检查,若不接收it程序会崩溃这里就先不展开。还有些编译器中erase数据可能会缩容等。如果开辟了新的空间,it就成了野指针,最好使用erase之后要接收一下返回值。.
在vs中,即是v的size在最后只有3,但是it最后的值任然是6
最后再次强调接收it的值,不然在部分编译器下会报错
2.vector的模拟实现
2.1vector类成员总览
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <assert.h>
using namespace std;
namespace clx
{
template<class T>
class vector
{
public:
//iterator
typedef T* iterator;
typedef const T* const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
vector();
vector(const vector<T>& v);
vector<T>& operator=(vector<T> v);
~vector();
//capacity
size_t size() const;
size_t capacity() const;
void reserve(size_t n);
void resize(size_t n, const T& val = T());
bool empty() const;
//modify
void push_back(const T& val);
void pop_back();
void insert(iterator pos, const T& val);
iterator erase(iterator pos);
void my_swap(vector<T>& v);
//see
T& operator[](size_t i);
const T& operator[](size_t i) const;
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
void vector_test1();
void vector_test2();
void vector_test3();
template<class T>
void vector_print(const vector<T>& v);
}
为了方便我们的vector可以存储各种各样的数据,我们使用类模板来定义vector
vector的成员变量是三个类型为T*的指针
_start 指向顺序表开头的元素
_finish 指向顺序表最后一个元素的后面一个位置
_endofstorage指向顺序表容量最大位置的后一个位置
2.2vector类迭代器
template<class T>
typename clx::vector<T>::iterator clx::vector<T>::begin()
{
return _start;
}
template<class T>
typename clx::vector<T>::iterator clx::vector<T>::end()
{
return _finish;
}
template<class T>
typename clx::vector<T>::const_iterator clx::vector<T>::begin() const
{
return _start;
}
template<class T>
typename clx::vector<T>::const_iterator clx::vector<T>::end() const
{
return _finish;
}
2.3vector默认成员函数
1.默认构造函数
template<class T>
clx::vector<T>::vector()
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}
2.拷贝构造函数
以前的写法
template<class T>
clx::vector<T>::vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
if (v._start)
{
iterator _start = new T[v.capacity()]; //开辟和v相同大的空间
//for ( const auto& e : v)
//{
// push_back(e);
//}
for (size_t i = 0; i < size(); i++) //逐个元素赋值
{
_start[i] = v._start[i];
}
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
}
注意:这里可以使用范围for赋值,也可以使用for逐个赋值,但是不能使用memcpy来拷贝
现代写法
template<class T>
clx::vector<T>::vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
reserve(v.capacity());
for (auto e : v)
{
push_back(e);
}
}
注意:拷贝构造还是需要初始化的,若不初始化,我们的tmp在析构时内部成员变量是随机值,析构时会产生非法访问内存
3.operator=
以前写法
template<class T>
clx::vector<T>& clx::vector<T>::operator=(const vector<T>& v)
{
if (this != &v)
{
delete[] _start;
_start = new T[v.capacity()];
//for (const auto& e : v)
//{
// push_back(e);
//}
for (size_t i = 0; i < size(); i++)
{
_start[i] = *(v._start + i);
}
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
return *this;
}
现代写法
template<class T>
clx::vector<T>& clx::vector<T>::operator=(const vector<T> v)
{
my_swap(*this, v);
return *this;
}
现代写法讲形参并未加引用,则v就是实参的一份拷贝,让*this和v交换,得到实参的拷贝,并且在v出函数时,析构v
4.析构函数
template<class T>
clx::vector<T>::~vector()
{
if(_start)
{
delete[] _start;
}
_start = _finish = _endofstorage = nullptr; //将空间释放,将指针制空
}
2.4vector容量相关函数
size()
template<class T>
size_t clx::vector<T>::size() const
{
return _finish - _start; //得到元素个数
}
获取元素个数
capacity()
template<class T>
size_t clx::vector<T>::capacity() const
{
return _endofstorage - _start;
}
获取容量大小
template<class T>
void clx::vector<T>::reserve(size_t n)
{
if (n > capacity()) //若n大于容量
{
iterator tmp = new T[n]; //扩容
size_t sz = size(); //获取原来元素的个数
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * sz);
for (size_t i = 0; i < sz; i++) //将元素逐个拷贝
{
tmp[i] = _start[i];
}
delete[] _start; //释放旧空间
}
_start = tmp; //让指针指向正确位置
_finish = tmp + sz;
_endofstorage = tmp + n;
}
}
改变容量
> 注意:创建sz变量来存储元素个数,是因为当_start = tmp时,_finish还未指向正确的位置,使用size()函数需要_start和_finish的位置,此时_start 是新空间的_start,而_finish是旧空间的_finish,两者毫无关系
memcpy局限性(在博客第四节会详细说明)
resize()
template<class T>
void clx::vector<T>::resize(size_t n, T val)
{
if (n < size()) //若比原元素数量还少,直接移动_finish
{
_finish = _start + n;
}
else
{
if ( n > capacity()) //若比容量大,则扩容
{
reserve(n);
}
while (_finish != _endofstorage) //将_finish 到_endofstorage左开右闭区间初始化成val
{
*_finish = val;
++_finish;
}
}
}
增容并且初始化
empty()
template<class T>
bool clx::vector<T>::empty() const
{
return size() == 0;
}
判断容器是否为空
2.5 vector类增删查改
push_back
template<class T>
void clx::vector<T>::push_back(const T& x)
{
if (_finish == _endofstorage) //如果元素数量等于容量,则进行扩容
{
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
}
*_finish = x; //尾部插入x并且让元素个数加一
_finish++;
}
insert()
template<class T>
void clx::vector<T>::insert(iterator pos, const T& val)
{
size_t sz = pos - _start;
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
pos = _start + sz;
}
iterator end = _finish;
while (end != pos)
{
*end = *(end - 1);
end--;
}
*pos = val;
_finish++;
}
在insert过程中,若发生了增容,那么vector的地址将会发生改变,形参pos就会成为野指针。这就是我们开始说的迭代器失效,为了避免我们可以在insert增容后重新给pos赋值,更新pos的含义。也可以想上面一样,找到pos和_start的相对距离,进行元素插入
pop_back()
template<class T>
void clx::vector<T>::pop_back()
{
assert(size() > 0);
_finish--;
}
erase()
template<class T>
typename clx::vector<T>::iterator& clx::vector<T>::erase(iterator pos)
{
iterator it = pos + 1;
while (it != _finish)
{
*(it - 1) = *it;
it++;
}
_finish--;
return pos;
}
这两个函数本身都比较简单。但是在这个地方,因为我们的声明和定义是分离的,编译器并不知道我们的返回值clx::vector::iterator&是一个类型,他可能是一个静态成员函数,也可能是一个静态变量,所以我们要在前面加上一个typename来告诉编译器这是一个类型
2.6 swap 和 operator[ ]
swap()
template<class T>
void clx::vector<T>::my_swap(vector<T>& v)
{
::swap(_start, v._start);
::swap(_finish, v._finish);
::swap(_endofstorage, v._endofstorage);
}
operator[ ]
template<class T>
const T& clx::vector<T>::operator[](size_t i) const
{
assert(i < size());
return *(_start + i);
}
template<class T>
T& clx::vector<T>::operator[](size_t i)
{
assert(i < size());
return *(_start + i);
}
2.7memcpy的局限性
在模拟实现的过程中,并没有使用memcpy进行拷贝。这是因为memcpy是通过字节长度进行的浅拷贝。若vector中所存数据为自定义类型,如string。那么memcpy进行拷贝的新空间上的数据还是原来的数据,也就是说前后两块空间的string为同一个string。此时我们释放就空间就会将string释放,现在的顺序表中的string类中的指针就变成了野指针。
3.vector类模拟源码
3.1Makefile
test:test.o vector.o
g++ -o $@ $^ -std=c++11
test.o:test.cpp
g++ -c -g $< -std=c++11
vector.o:vector.cpp
g++ -c -g $< -std=c++11
.PHONY:clean
clean:
rm -f ./*.o test
3.2vector.h
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <assert.h>
using namespace std;
namespace clx
{
template<class T>
class vector
{
public:
//iterator
typedef T* iterator;
typedef const T* const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
vector();
vector(const vector<T>& v);
vector<T>& operator=(vector<T> v);
~vector();
//capacity
size_t size() const;
size_t capacity() const;
void reserve(size_t n);
void resize(size_t n, const T& val = T());
bool empty() const;
//modify
void push_back(const T& val);
void pop_back();
void insert(iterator pos, const T& val);
iterator erase(iterator pos);
void my_swap(vector<T>& v);
//see
T& operator[](size_t i);
const T& operator[](size_t i) const;
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
void vector_test1();
void vector_test2();
void vector_test3();
template<class T>
void vector_print(const vector<T>& v);
}
3.3vector.cpp
#include "vector.h"
template<class T>
typename clx::vector<T>::iterator clx::vector<T>::begin()
{
return _start;
}
template<class T>
typename clx::vector<T>::iterator clx::vector<T>::end()
{
return _finish;
}
template<class T>
typename clx::vector<T>::const_iterator clx::vector<T>::begin() const
{
return _start;
}
template<class T>
typename clx::vector<T>::const_iterator clx::vector<T>::end() const
{
return _finish;
}
template<class T>
void clx::vector<T>::my_swap(vector<T>& v)
{
::swap(_start, v._start);
::swap(_finish, v._finish);
::swap(_endofstorage, v._endofstorage);
}
template<class T>
clx::vector<T>::vector()
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}
template<class T>
clx::vector<T>::vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
reserve(v.capacity());
for (auto e : v)
{
push_back(e);
}
}
template<class T>
clx::vector<T>& clx::vector<T>::operator=(vector<T> v)
{
swap(v);
return *this;
}
template<class T>
clx::vector<T>::~vector()
{
if (_start)
{
delete[] _start;
}
_start = _finish = _endofstorage = nullptr;
}
template<class T>
size_t clx::vector<T>::size() const
{
return _finish - _start;
}
template<class T>
size_t clx::vector<T>::capacity() const
{
return _endofstorage - _start;
}
template<class T>
void clx::vector<T>::reserve(size_t n)
{
if (n > capacity())
{
iterator tmp = new T[n];
size_t sz = size();
for (size_t i = 0; i < size(); i++)
{
tmp[i] = _start[i];
}
delete[] _start;
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n;
}
}
template<class T>
void clx::vector<T>::resize(size_t n, const T& val)
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
iterator it = _finish;
while (_finish != _endofstorage)
{
*it = val;
it++;
}
}
if (n < size())
{
_finish = _start + n;
}
}
template<class T>
void clx::vector_print(const vector<T>& v)
{
typename vector<T>::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
template<class T>
bool clx::vector<T>::empty() const
{
return size() == 0;
}
template<class T>
void clx::vector<T>::push_back(const T& val)
{
if (_endofstorage == _finish )
{
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
}
*_finish = val;
_finish++;
}
template<class T>
void clx::vector<T>::pop_back()
{
if (!empty())
{
_finish--;
}
}
void clx::vector_test1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
vector_print(v);
}
template<class T>
void clx::vector<T>::insert(iterator pos, const T& val)
{
size_t sz = pos - _start;
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
pos = _start + sz;
}
iterator end = _finish;
while (end != pos)
{
*end = *(end - 1);
end--;
}
*pos = val;
_finish++;
}
template<class T>
typename clx::vector<T>::iterator clx::vector<T>::erase(iterator pos)
{
assert(!empty());
iterator it = pos;
while (pos < _finish - 1)
{
*it = *(it + 1);
}
_finish--;
return pos;
}
void clx::vector_test2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector_print(v);
vector<int>::iterator pos = find(v.begin(), v.end(), 4);
v.insert(pos, 5);
vector_print(v);
}
template<class T>
const T& clx::vector<T>::operator[](size_t i) const
{
assert(i < size());
return *(_start + i);
}
template<class T>
T& clx::vector<T>::operator[](size_t i)
{
assert(i < size());
return *(_start + i);
}
void clx::vector_test3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (size_t i = 0; i < v.size() - 1; i++)
{
cout << v[i] << " ";
}
cout << endl;
}