Vector是一个封装了动态大小数组的顺序容器(Sequence Container)。可以简单的认为,vector是一个能够存放任意类型的动态数组。
vector是一个类模板,其中封装了类的多种方法。例如,在使用时要改变大小,它的大小容器会自动处理。字符串的增删查改等操作,通过调用类中的方法便捷的进行操作…。
(constructor)构造函数声明 | 接口说明 |
---|---|
vector() | 无参构造 |
vector(size_t n, const T& val = T() | 构造并初始化n个val |
vector(const vector& x) | 拷贝构造 |
vectorvector(InputIterator first, InputIterator last) | 使用迭代器进行初始化构造 |
test:
void test_vector_constructor()
{
//因为vector是一个模板类,所以string也可以当参数的类型
//无参构造
vector<string> v1;
string name("ren");
//正常尾插一个string类对象
v1.push_back(name);
//匿名对象
v1.push_back(string("yv"));
//隐式类型转换
v1.push_back("qing");
//构造并初始化n个val
vector<string> v2(5, "xxx");
//拷贝构造
vector<string> v3(v1);
//使用迭代器初始化构造
string str("hello world");
vector<char> v4(str.begin(), str.end());
vector<int> v5(str.begin(), str.end());//用char初始化int会有整型提升,类型转换。 ASCII
//使用数组构造
int a[] = { 16,2,77,29 };
vector<int> v6(a, a + 4); //a本质是指针
}
class A
{
public:
A( int a = 0){
cout << "构造函数" << endl;
}
A(const A& x){
cout << "拷贝构造" << endl;
}
~A(){
cout << "析构函数" << endl;
}
};
int main()
{
vector<A> va{ A(10),A(20),A(30),A(40) };
cout << va.size() << endl;
cout << va.capacity() << endl;
return 0;
}
注意:
vector
两者的区别:v{ 7, 8, 9 };和vector v = { 7, 8, 9 };
这两个表达方式实际上是等价的,它们都是用于创建一个包含整数的vector。两者的不同之处在于初始化的方法不同。
在第一个例子中,你使用的是初始化列表(initializer list)的方式,通过大括号{}将元素括起来,并直接将它赋值给vector v。这是C++11引入的一种方便的初始化方法。
而在第二个例子中,你使用的是赋值操作符(assignment operator)来将初始化列表赋值给vector v。这种方式更加传统,在C++11之前就可以使用。
总的来说,这两种方式都可以用于创建和初始化vector,并且具有相同的效果。所以你可以根据个人偏好来选择使用哪种方式。
iterator的使用 | 接口说明 |
---|---|
begin + end | 获取第一个数据位置的iterator/const_iterator,获取最后一个数据的下一个位置的iterator/const_iterator |
rbegin + rend | 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator |
test:
注意: 在string中说过,迭代器实现了之后,范围for可以直接使用。范围for本质就是迭代器
void test_iterator()
{
int a[] = { 16, 2, 77, 239, 23, 3, 43, 2, 3, 3, };
int size = sizeof(a) / sizeof(a[0]);
vector<int> v1(a, a + size);
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
vector<int>::reverse_iterator rit = v1.rbegin();
while (rit != v1.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
函数名称 | 接口说明 |
---|---|
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
reserve | 改变vector的capacity |
resize | 改变vector的size |
reserve负责开空间,如果预先知道需要使用开多少空间,reserve可以缓解vector增容的代价。
test:
void test_space()
{
vector<int> v1;
v1.resize(10);
for (size_t i = 0; i < 10; i++)
{
//使用[]改变
v1[i] = i;
}
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
cout << " size:" << v1.size() << endl;
cout << "capacity:" << v1.capacity() << endl;
cout << "empty:" << v1.empty() << endl << endl;
vector<int> v2;
v2.reserve(10);
for (size_t i = 0; i < 5; i++)
{
//尾插
v2.push_back(i);
}
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
cout << " size:" << v2.size() << endl;
cout << "capacity:" << v2.capacity() << endl;
v2.clear();
cout << "empty:" << v2.empty() << endl;
}
test(vector默认扩容机制):vector的增容是1.5倍
void test_vector_expand()
{
size_t sz;
vector<int> v;
sz = v.capacity();
for (size_t i = 0; i < 100; i++)
{
v.push_back(i);
if (sz != v.capacity())
{
sz = v.capacity();
cout << "New capacity:" << sz << endl;
}
}
}
函数名称 | 接口说明 |
---|---|
push_back | 尾插 |
pop_back | 尾删 |
erase | 删除pos位置的数据 |
insert | 在pos之前插入val |
swap | 交换两个vector的数据空间 |
operator[] | 像数组一样访问 |
test:
void test_modifiers()
{
vector<int> v;
//push_back
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 (auto e : v)
{
cout << e << " ";
}
cout << endl;
//删除操作
v.pop_back();
v.erase(v.begin());
v.erase(v.end() - 1); //vector可以直接减1,list不行
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
//insert
v.insert(v.begin(), 3);
v.insert(v.end(), 10);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
//swap
vector<int> v1;
v1.swap(v); //v现在没有值
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
for (size_t i = 0; i < v1.size(); i++)
{
//operator
v1[i]++;
cout << v1[i] << " ";
}
cout << endl;
}
引起底层空间改变的操作,都有可能是迭代器失效
这里使用insert和erase举例,当然别的方法也存在这个情况,例如:resize,reserve,push_back…。
解决迭代器失效的方法:在使用前,对迭代器重新赋值即可。
test:
void test_iterator()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
//vector::iterator it = v.begin();
//v.insert(it, 100);
//高危行为
//*it += 10;
//删除pos位置的值,导致迭代器失效
/*vector::iterator pos = find(v.begin(), v.end(), 3);
v.erase(pos);
cout << *pos << endl;*/
//如何解决问题:对迭代器重新赋值
//eg:删除所有偶数
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
//因为erase在调用完成后返回pos的后一个位置,
//如果pos是最后一个位置则这是容器结束
it = v.erase(it);
}
}
}
在构造函数里涉及到自定义类型元素中的资源管理。
如果在vector< string >类的单参构造中,使用memcpy。如果仅仅对自定义类型的元素进行拷贝完全没问题,但是拷贝的自定义类型元素中涉及到资源管理就会出现问题。
以拷贝构造举例,这里就不能使用memcpy了,而是一步步遍历赋值
vector(const vector<T>& v)
{
_start = new T[v.capacity()];
//如果是vector 在拷贝时需要是深拷贝
//memcpy(_start, v._start, sizeof(T) * v.size());
for (size_t i = 0; i < v.size(); i++)
{
//_start[i] = v._start[i];
_start[i] = T(v._start[i]);
}
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
所以在实现单参构造函数时,不能使用memcpy
在vector类实现中,我们采用和库中相同的成员变量,三个迭代器,在前面的动态二维数组知识点提到。
private:
//这里最好给默认值,这样在构造函数就可以不用刻意初始化为nullptr
//使用未定义的值进行构造会出现大问题
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;
_finish - _start = size();
_end_of_storage - _start = capacity();
namespace kpl
{
template<class T>
class vector
{
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
// 无参构造
vector()
//: _start(nullptr) //这里都可以省略,因为在成员变量声明的时候给了默认值nullptr
//, _finish(nullptr)
//, _endOfStorage(nullptr)
{}
vector(size_t n, const T& value = T())
{
/*reserve(n);
while (n--)
{
push_back(value);
}*/
resize(n, value);
}
//vector v(10, 1);
//vector v(10u, 1);
//vector v(10, "11111");
//解决例子匹配问题
vector(int n, const T& value = T())
{
resize(n, value);
}
// 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器
// 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
//拷贝构造
vector(const vector<T>& v)
{
_start = new T[v.capacity()];
//如果是vector 在拷贝时需要是深拷贝
//memcpy(_start, v._start, sizeof(T) * v.size());
for (size_t i = 0; i < v.size(); i++)
{
//_start[i] = v._start[i];
_start[i] = T(v._start[i]);
}
_finish = _start + v.size();
_endOfStorage = _start + v.capacity();
}
//运算符重载,现代写法
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endOfStorage , v._endOfStorage );
}
vector<T>& operator=(vector<T> v)
{
//给成员变量都初始化成nullptr的好处就体现出来了,如果交换之后是一个随机的地址就会出现错误
swap(v);
return *this;
}
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _endOfStorage = nullptr;
}
}
// 迭代器相关
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
// 容量
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endOfStorage - _start;
}
bool empty() const
{
return _start == _finish;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
// 1. 开辟新空间
T* tmp = new T[n];
// 2. 拷贝元素
if (_start)
{
for (size_t i = 0; i < sz; ++i)
tmp[i] = _start[i];
// 3. 释放旧空间
delete[] _start;
}
_start = tmp;
//这里不能使用size(),因为size的实现是_finish-_start
//这时_start改变了,但是finish还没改变
_finish = _start + sz;
_endOfStorage = _start + n;
}
}
void resize(size_t n, const T& value = T())
{
if (n <= size())
{
_finish = _start + n;
return;
}
else
{
//增容
reserve(n);
//赋值
while (_finish != _start + n)
{
*_finish = value;
++_finish;
}
}
}
// 元素访问
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
T& front()
{
return *_start;
}
const T& front()const
{
return *_start;
}
T& back()
{
return *(_finish - 1);
}
const T& back()const
{
return *(_finish - 1);
}
// vector的修改操作
void push_back(const T& x)
{
//if (_finish == _endOfStorage)
//{
// size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
// reserve(newcapacity);
//}
//*_finish = x;
//++_finish;
insert(end(), x);
}
void pop_back()
{
erase(end() - 1);
}
//vector erase和insert迭代器对象使用后,不能在访问这个迭代器
//我们认为它失效了,访问的结果是未定义的
iterator insert(iterator pos, const T& x)
{
assert(pos <= _finish && pos >= _start);
// 空间不够先进行增容
if (_finish == _endOfStorage)
{
//保存pos的相对位置
size_t len = pos - _start;
size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;
reserve(newCapacity);
//解决pos迭代器失效问题,对pos位置更新
pos = _start + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
// 返回删除数据的下一个数据,如果是最后一个元素,则是容器结束
// 方便解决:一边遍历一边删除的迭代器失效问题
iterator erase(iterator pos)
{
assert(pos < _finish && pos >= _start);
iterator it = pos + 1;
while (it != _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
private:
iterator _start = nullptr; // 指向数据块的开始
iterator _finish = nullptr; // 指向有效数据的尾
iterator _endOfStorage = nullptr; // 指向存储容量的尾
};
}