版权声明:本文为博主原创文章,欢迎转载,转载请声明出处! https://blog.csdn.net/hansionz/article/details/84206109
C++的vector学习及模拟实现
一.vector的介绍及使用
1.什么是vector
vector
是表示可变大小
数组的序列容器
,相当于一个动态的顺序表
vector
也采用的连续存储空间
来存储元素。可以采用下标对vector的元素
进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变
的,而且它的大小会被容器自动处理
。vector
使用动态分配数组
来存储它的元素。当新元素插入
时候,为了增加存储空间这个数组需要被重新分配大小
。其做法是,分配一个新的数组
,然后将全部元素移到
这个数组。vector
会分配一些额外的空间
以适应可能的增长
,因为存储空间
比实际需要的存储空间更大
。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小
,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的
。vector
占用了更多的存储空间,为了获得管理存储空间的能力
,并且以一种有效的方式动态增长
。- 与其它
动态序列容器
相比,vector
在访问元素的时候更加高效
,在末尾添加和删除元素
相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists
和forward_lists
统一的迭代器
和引用更好
。
2.使用vector
2.1 vector常见的构造函数
vector()
:无参无内容的构造vector(size_type n, const value_type& val = value_type())
:构造大小为n个的vector并初始化为val
vector (const vector& x)
:拷贝
构造vector (InputIterator first, InputIterator last)
:使用迭代器
进行初始化构造
void test1()
{
vector<int> v1;//无参构造
cout << v1.size() << endl;//0
vector<int> v2(4, 12);//构造大小为4的vector并初始化12
cout << v2.size() << endl;//4
vector<int> v3(v2);//拷贝构造
cout << v3.size() << endl;//4
vector<int> v4(v2.begin(), v2.end());//迭代器构造
cout << v4.size() << endl;//4
}
2.2 vector的迭代器(iterator)
begin()
:获取第一个
位置的iterator
end()
:获取最后一个
数据的下一个位置
的iterator
rbegin()
:获取最后一个数据位置
的reverse_iterator
rend()
:获取第一个数据前一个位置
的reverse_iterator
cbegin()
:获取第一个数据位置
的const_iterator(不可修改)
cend()
:获取最后一个数据
的下一个位置
的const_iterator(不可改变)
迭代器的位置:
测试代码:
void test2()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//iterator遍历打印1234
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it;
++it;
}
cout << endl;
//iterator修改数据2468
while (it != v.end())
{
*it *= 2;
++it;
}
//reverse_iterator遍历打印4321
vector<int>::reverse_iterator rit = v.rbegin();
while (rit != v.rend())
{
cout << *rit;
++rit;
}
cout << endl;
//const_iterator遍历打印1234
vector<int>::const_iterator cit = v.cbegin();
while (cit != v.cend())
{
cout << *cit;
//*cit *= 2;不可以修改
++cit;
}
cout << endl;
//const_reverse_iterator遍历打印4321
vector<int>::const_reverse_iterator crit = v.crbegin();
while (crit != v.crend())
{
cout << *crit;
//*crit *= 2;不能修改
++crit;
}
cout << endl;
}
2.3 vector空间增长常见接口
size()
:获取有效元素
的个数capacity()
:获取容量
的大小empty()
:判断是否为空
void resize (size_type n, value_type val = value_type())
:修改size
的大小void reserve (size_type n)
:修改容量
的大小
注意:
vector
的capacity
在vs
下是按1.5倍增长
的,g++
是按2倍增长
的(vs是PJ版本STL,g++是SGI版本STL)
。reserve
只负责开辟空间,如果确定知道需要用多少空间,reserve
可以缓解vector
增容的代价缺陷问 题。resize
在开空间的同时还会进行初始化
void test3()
{
//测试vs下的增容
vector<int> v;
size_t s = v.capacity();
for (int i = 0; i < 1000; i++)
{
v.push_back(i);
if (s != v.capacity())
{
s = v.capacity();
cout << "容量改变:" << s << endl;
}
}
}
运行结果如下:可以看出vs下的增容是按照1.5倍去增的
void test4()
{
//测试reserve
vector<int> v;
size_t s = v.capacity();
v.reserve(100);
for (int i = 0; i < 100; i++)
{
v.push_back(i);
if (s != v.capacity())
{
s = v.capacity();
cout << "容量改变:" << s << endl;
}
}
}
运行结果为只增容一次,所以如果实现直到大小,可以使用reserve
来减少增容带来的开销。
2.4 vector常见的增删查改接口
void push_back (const value_type& val)
:尾插void pop_back()
:尾删InputIterator find (InputIterator first, InputIterator last, const T& val)
:查找(注意这个是算法模块实现,不是 vector的成员接口)
iterator insert (iterator position, const value_type& val)
:在position之前插入valiterator erase (iterator position)
:删除position位置的数据void swap (vector& x)
:交换两个vector
的数据空间operator[] (size_type n)
:重载[]
,像数组一样访问vector
void test7()
{
int arr[] = { 1, 2, 3, 4 };
//相当于迭代器区间构造
vector<int> v(arr, arr + (sizeof(arr) / sizeof(arr[0])));
v.push_back(5);//12345
v.push_back(6);//123456
v.pop_back();//12345
//查找vector中3所在的迭代器
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
v.insert(pos, 8);//128345(在pos前插入20)
pos = find(v.begin(), v.end(), 2);
v.erase(pos);//18345(删除2)
}
void test8()
{
vector<int> v1(4, 10);
vector<int> v2(5, 20);
cout << "***交换前***" << endl;
//10 10 10 10
for (size_t i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
//20 20 20 20 20
for (size_t i = 0; i < v2.size(); i++)
{
cout << v2[i] << " ";
}
cout << endl;
v1.swap(v2);
cout << "***交换后***" << endl;
//20 20 20 20 20
for (size_t i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
//10 10 10 10
for (size_t i = 0; i < v2.size(); i++)
{
cout << v2[i] << " ";
}
cout << endl;
}
2.5 vector的三种遍历方式
迭代器
遍历operator[] + for
循环遍历auto + for
循环遍历(C++11语法糖)
void test9()
{
int arr[] = { 1, 2, 3, 4 };
vector<int> v(arr, arr + (sizeof(arr) / sizeof(arr[0])));
//1.operator[] + for
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
//2.迭代器
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//3.auto + for(C++11新特性)
//此处尽量加上const和引用,防止拷贝带来的代价和该值被修改
for (const auto& e : v)
{
cout << e << " ";
}
cout << endl;
}
2.6 迭代器失效问题
erase
导致的迭代器失效
void test10()
{
int arr[] = { 1, 2, 3, 4 };
vector<int> v(arr, arr + (sizeof(arr) / sizeof(arr[0])));
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
v.erase(pos);//erase删除完一个数据之后,返回下一个数据的迭代器
cout << *pos << endl;//但是程序在vs下不能正常运行,可能是因为vs下的机制检查的比较严格,迭代器会失效,不能正常访问。但是在Linux下的g++编译器不会发生迭代器失效,可以正常访问。
}
insert
导致迭代器失效
void test11()
{
int arr[] = { 1, 2, 3, 4 };
vector<int> v(arr, arr + (sizeof(arr) / sizeof(arr[0])));
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
v.insert(pos, 8);
//insret后会导致vector扩容,扩容的方法是从新开辟空间,然后将数据拷贝过去
//在释放原来的空间,那么在对原来的迭代器访问就会出现非法访问
cout << *pos << endl;
}
- 一个常见迭代器失效的场景
void test12()
{
// 下面的代码实现删除v中的所有偶数
// 下但是结果会崩溃掉,如果是偶数,erase导致it失效
// 对失效的迭代器进行++it,会导致程序崩溃
int arr[] = { 1, 2, 3, 4 };
vector<int> v(arr, arr + (sizeof(arr) / sizeof(arr[0])));
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
v.erase(it);
}
++it;
}
}
//上边的代码可以改成下面这样
void test13()
{
int arr[] = { 1, 2, 3, 4 };
vector<int> v(arr, arr + (sizeof(arr) / sizeof(arr[0])));
vector<int>::iterator it = v.begin();
while (it != v.end())
{
vector<int>::iterator end = v.end();
if (*it % 2 == 0)
{
it = v.erase(it);//erase调用完之后,返回下一个元素的迭代器,在vs(检查较为严格)下需要使用it重新接受迭代器,但是在g++编译器下不用
}
else
{
++it;
}
}
}
二.vector的深度理解及核心接口的模拟实现
1.vector的深度理解
2.vector类核心接口的模拟实现
namespace zsc
{
template<class T>
class Vector
{
public:
/**
* 模拟实现迭代器核心接口(使用大写和vector区分)
*/
typedef T* Iterator;
typedef const T* Const_Iterator;
Iterator Begin()
{
return _start;
}
Iterator End()
{
return _finish;
}
Const_Iterator CBegin() const
{
return _start;
}
Const_Iterator CEnd() const
{
return _finish;
}
/**
* 容量核心接口的模拟实现
*/
size_t Size() const
{
return _finish - _start;
}
size_t Capacity() const
{
return _endofstorage - _start;
}
void Reserve(size_t n)
{
if(n > Capacity())
{
size_t size =Size();
T* tmp = new T[n];
//使用memcpy会出现问题,当vector内如果存的是char*的情况
//memcpy按照字节拷贝,只是简单的把指向字符串的char*拷贝过来
//在释放原来的空间时,字符串已经被销毁了
//if(_start)
// memcpy(tmp, _start, sizeof(T)*size);
if(_start)//第一次扩容时,_start为空
{
for(size_t i =0; i < size; i++)
tmp[i] = _start[i];
}
delete[] _start;
_start = tmp;
_finish = _start + size;
_endofstorage = _start + n;
}
}
void Resize(size_t n, const T& val = T())
{
//1.n小于等于当前vector的size,则在n的地方截断,有效数据为前n个
if(n < Size())
{
_finish = _start + n;
}
else
{
//如果n大于当前容量,考虑扩容问题
if(n > Capacity())
{
Reserve(n);
}
Iterator it = _finish;
_finish = _start + n;
//把大于size那部分填充成val
while(it < _finish)
{
*it = val;
++it;
}
}
}
/**
* 增删改查等核心接口的模拟实现
*/
Iterator Insert(Iterator pos, const T& val)
{
assert(pos <= _finish);
//检查容量,不够则扩容
if(_finish == _endofstorage)
{
size_t offset = pos - _start;
size_t newcapacity = Capacity() == 0 ? 2 : Capacity()*2;
Reserve(newcapacity);
//增容需要重置pos
pos = _start + offset;
}
Iterator end = _finish - 1;
while(end >= pos)
{
*(end+1) = *end;
--end;
}
*pos = val;
++ _finish;
return pos;
}
//Erase删除一个数据后,返回下一个数据的迭代器
Iterator Erase(Iterator pos)
{
assert(pos < _finish);
Iterator begin = pos + 1;
while(begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
return pos;
}
void PushBack(const T& val)
{
Insert(End(), val);
}
void PopBack()
{
Erase(End() - 1);
}
/**
* 各类构造函数的模拟实现
*/
//无参构造
Vector()
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}
//构造n个val
Vector(size_t n, const T& val = T())
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
Reserve(n);//先将容量增加到n,可以减少多次增容带来的代价
while(n--)
{
PushBack(val);
}
}
//迭代器区间构造
//template<class InputIterator>
Vector(Iterator begin, Iterator last)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
Reserve(last - begin);
while(begin != last)
{
PushBack(*begin);
++begin;
}
}
void Swap(Vector& v1)
{
swap(_start, v1._start);
swap(_finish, v1._finish);
swap(_endofstorage, v1._endofstorage);
}
Vector<T>& operator=(Vector<T> v)
{
Swap(v);
return *this;
}
T& operator[](size_t pos)
{
assert(pos < Size());
return _start[pos];
}
//拷贝构造
Vector(const Vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
Reserve(v.Capacity());
Iterator it = Begin();
Const_Iterator cit = v.CBegin();
while(cit != v.CEnd())
{
*it = *cit;
++it;
++cit;
}
_finish = _start + v.Size();
_endofstorage = _start + v.Capacity();
}
~Vector()
{
if(_start)
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
}
/**
* 返回val位置的迭代器
*/
Iterator Find(const T& val)
{
Iterator it1 = Begin();
while(it1 != End())
{
if(*it1 == val)
return it1;
++it1;
}
}
private:
T* _start;
T* _finish;
T* _endofstorage;
};
}
代码位于:https://github.com/hansionz/Cpp_Code/tree/master/Vector