【C++】vector
vector简介
- vector是一个表示可变大小数组的序列容器
- vector的底层存储和数组类似,采用连续的空间存储数据。这就意味着vector支持下标访问元素
- vector的空间大小是动态可变的,当新元素插入,空间不够时,它可以自动扩容
- vector会分配一些额外的空间来适应数据的增长,所以vector使用的空间比实际需要的空间要大
- 与其他动态序列容器相比,vector访问元素的效率较高,尾插尾删数据的效率也很不错;但是对于其他位置的修改,效率就很低了
vector类的常用接口
在使用容器时,查询文档是必不可少的:vector相关文档
下面列举一些常用的接口
构造
接口 | 接口说明 |
---|---|
vector() | 无参构造 |
vector(size_type n, const value_type& val = value_type ()) | 构造并初始化 n 个 val |
vector(const vector& v) | 拷贝构造 |
vector(InputIterator first, InputIterator last) | 使用迭代器进行初始化构造 |
void test1()
{
// 无参
vector<int> v1;
// n val
vector<int> v2(10, 1);
// 拷贝构造
vector<int> v3(v2);
// 迭代器区间构造
string s("abcd");
vector<int> v4(s.begin(), s.end());
for (auto& e : v1)
cout << e << " ";
cout << endl;
for (auto& e : v2)
cout << e << " ";
cout << endl;
for (auto& e : v3)
cout << e << " ";
cout << endl;
for (auto& e : v4)
cout << e << " ";
cout << endl;
}
迭代器
接口 | 接口说明 |
---|---|
begin() | 获取第一个数据位置的 iterator |
end() | 获取最后一个数据的下一个位置的 iterator |
rbegin() | 获取最后一个数据位置的 reverse_iterator |
rend() | 获取第一个数据前一个位置的 reverse_iterator |
Capacity
接口 | 接口说明 |
---|---|
size() | 获取数据个数。返回值为容器中当前实际存储的元素个数。 |
capacity() | 获取容量大小。表示在不重新分配内存的情况下,容器可以容纳的元素数量。 |
empty() | 判断是否为空。如果容器中没有元素,则返回 true,否则返回 false。 |
resize() | 改变 vector 的 size。可以指定新的大小,如果新大小小于当前大小,容器会删除多余的元素;如果新大小大于当前大小,新添加的元素会进行值初始化(对于基本类型初始化为 0,对于类类型会调用默认构造函数)。 |
reserve() | 改变 vector 的 capacity。可以预先分配一定的内存空间,避免在添加元素时频繁进行内存重新分配操作。如果指定的容量小于当前容量,则此函数不做任何操作。 |
增删查改
接口 | 接口说明 |
---|---|
push_back() | 尾插 |
pop_back() | 尾删 |
find(算法模块实现) | 查找。查找特定元素,返回指向该元素的迭代器,如果未找到则返回尾迭代器。 |
insert | 在指定位置之前插入 val。可以插入单个元素或一段范围的元素。 |
erase | 删除 position 位置的数据。可以删除单个位置的元素,也可以删除一个范围内的元素。 |
swap | 交换两个 vector 的数据空间。快速交换两个向量的内容,而不需要逐个元素复制。 |
operator[] | 像数组一样访问。可以通过下标访问向量中的元素,但不进行边界检查,使用时需确保下标在合法范围内。 |
vector类的模拟实现
模板类
因为vector中可以存储不同类型的数据:内置类型、自定义类型,所以我们要将vector写成模板类
template <class T>
class vector
{};
成员变量
在vector中,许多功能都要依赖迭代器来实现,所以迭代器是很重要的
vector的迭代器底层是原生指针,所以vector的iterator可以使用 T* 来实现
template <class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
};
在string的模拟实现中,我们用到了三个成员变量:
- char* _str:字符数组的地址,用来存储字符串
- size_t _size:用来表示字符串的实际长度
- size_t _capacity:用来表示空间的总大小
模拟实现vector,我们用的三个变量和string的类似,只不过用的都是迭代器,而vector的迭代器底层就是原生指针,也就是我们的三个成员变量都是指针
- iterator _start:指向第一个数据
- iterator _finish:指向最后一个数据的下一个位置
- iterator _endofstorage:指向空间的末尾
虽然换了一种形式,但是vector的成员变量和string的成员变量代表的含义是类似的
搭一个框架
先把无参构造、size()、capacity()、operator[]、push_back()这几个功能实现,搭一个能跑的框架
无参构造
不传参数构造一个对象,那么对象中就是空的,所以先不开空间,直接给三个成员变量赋值为空,等插入数据时再做开空间等操作
可以在声明时给成员变量缺省值,这里的缺省值是给初始化列表准备的,这样进入构造函数,走初始化列表时,就不用我们手动写了
template <class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector() {}
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endofstorage = nullptr;
};
size && capacity
size_type size() const;
size_type capacity() const;
- 两指针相减即可得到长度
- size = _finish - _start
- capacity = _endofstorage - _start
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
operator[]
reference operator[] (size_type n);
const_reference operator[] (size_type n) const;
返回指定下标的元素的引用
- 可以加一个断言,防止越界操作
- 因为底部是数组存储,所以直接返回 _start[n] 即可
T& operator[](size_t n)
{
assert(n >= 0);
assert(n < size());
return _start[n];
}
const T& operator[](size_t n) const
{
assert(n >= 0);
assert(n < size());
return _start[n];
}
push_back()
void push_back (const value_type& val);
- 在插入数据之前,要检查空间是否已满,满了就要进行扩容。先在这里实现一个扩容,后面写了reserve再替换
- 扩容时,如果原来的对象为空,就给 4 个初始空间;否则就 2 倍扩容。拷贝旧空间的数据到新空间时,可以使用 memcpy
- 在尾部插入数据,更新 _finish
void push_back(const T& val)
{
// 检查扩容
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
// 申请新空间,拷贝旧数据
T* tmp = new T[newcapacity];
memcpy(tmp, _start, sizeof(T) * size());
delete[] _start;
// 更新数据
_start = tmp;
_finish = tmp + size();
_endofstorage = tmp + newcapacity;
}
// 插入数据
*_finish = val;
++_finish;
}
基本的架子搭建完成,先写一个打印vector的函数,测试一下插入数据
template <class T>
void PrintVector(const vector<T>& v)
{
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
cout << endl;
}
测试数据
void test1()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
PrintVector(v1);
}
测试结果:
程序出错了,哪一步出错了呢?进入调试,发现问题出在 push_back 中,尾插数据时出现了空指针错误
虽然我们构造了一个空的vector对象,_finish应该是空指针,但是经过扩容后_finish就不应该是空了。那么问题就是出在扩容了,再次进入调试,看看申请空间后,三个成员变量是否更新成功
可以看到,执行完扩容的代码,_start和_endofstorage都不再是空指针了,唯独_finish还是空指针,而更新_finish的语句是这一句:
_finish = tmp + size();
tmp肯定是没有问题的,那么答案显而易见,问题出在size()
size_t size() const
{
return _finish - _start;
}
原来是我们更新了_start的位置,但是 _finish 还留在原地,那么得到的size肯定是错误的
要解决这个问题,有两种办法
- 先更新_finish,再更新_start
// 更新数据
_finish = tmp + size();
_start = tmp;
_endofstorage = tmp + newcapacity;
- 一开始就记录旧的size,用旧size更新_finish,这里采用这个方法
// 更新数据
size_t old_size = size();
// ...
_start = tmp;
_finish = tmp + old_size;
_endofstorage = tmp + newcapacity;
测试:
既然完善了扩容,那接下来就写容量相关的接口吧
Capacity
size && capacity
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
empty
bool empty() const;
- 当 _start 和 _finish 指向同一个位置时,说明空间没有数据
bool empty() const
{
return _start == _finish;
}
reserve
void reserve (size_t n);
上面我们写 push_back 时已经完成了 reserve,直接拿来用
void reserve(size_t n)
{
if (n > capacity())
{
// 申请新空间,拷贝旧数据
T* tmp = new T[n];
memcpy(tmp, _start, sizeof(T) * size());
// 更新数据
size_t old_size = size();
_start = tmp;
_finish = tmp + old_size;
_endofstorage = tmp + n;
}
}
resize
void resize(size_t n, const T& val = T());
- 当 n <= size() 时,需要将有效数据删除到 n 个。不需要真的删除数据,只需要更新 _finish 即可
- 当 n > size() 时,需要填充数据为 val,这里的参数 val 有可能是自定义类型,所以缺省值是一个无参的匿名对象。内置类型也有构造函数,调用无参构造时会对内置类型对象初始化,例如整型初始化为0,指针初始化为nullptr
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;
}
}
}
迭代器
这里只实现begin()和end()以及它们的const版本
begin()
iterator begin();
const_iterator begin() const;
获取第一个数据位置的迭代器,也就是获取第一个数据的指针
iterator begin()
{
return _start;
}
const_iterator begin() const
{
return _start;
}
end()
iterator end();
const_iterator end() const;
获取最后一个数据下一个位置的迭代器
iterator end()
{
return _finish;
}
const_iterator end() const
{
return _finish;
}
测试:
增删查改
push_back
void push_back(const T& val)
{
// 检查扩容
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
}
// 插入数据
*_finish = val;
++_finish;
}
pop_back
void pop_back();
- 尾删一个元素,只需_finish减一即可
- 可以加一个断言,数据为空时不可以删除
void pop_back()
{
assert(!empty());
--_finish;
}
测试:
insert
iterator insert (iterator pos, const T& val);
在pos位置插入一个数据
- 加一个断言,防止越界,insert也可以支持头插尾插
- 在插入数据从之前,需要检查扩容
- 然后就需要挪动数据了,将pos位置及其后面的数据向后挪动一个单位
- 挪动完成后,更新数据即可
- 返回新插入数据的位置的迭代器,也就是pos
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
// 检查扩容
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
}
// 挪动数据
iterator cur = end();
while (cur > pos)
{
*cur = *(cur - 1);
--cur;
}
// 更新数据
*pos = val;
++_finish;
return pos;
}
测试:
但是这里有个隐藏bug,多插入几个数据就会出错
这是为什么呢?这两次测试的区别就是:第二次测试插入的数据较多,发生了扩容。怎么又是扩容的问题??
我们之前遇到的问题是空间发生了更新,然而指向原空间的指针并未更新,这次的问题也是这样:
- pos 指向原空间
- 如果发生了扩容,pos位置未更新,后面又使用pos进行操作,大概率会发生错误
- 所以如果需要扩容,先记录下pos相对_start的相对位置,扩容完成后更新pos
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
// 检查扩容
if (_finish == _endofstorage)
{
// pos相对位置
size_t len = pos - _start;
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
// 更新pos
pos = _start + len;
}
// 挪动数据
iterator cur = end();
while (cur > pos)
{
*cur = *(cur - 1);
--cur;
}
// 更新数据
*pos = val;
++_finish;
return pos;
}
测试:
erase
iterator erase (iterator pos);
删除pos位置的数据
- 断言,防止越界
- 将 pos 后面的数据向前挪动一个单位
- 返回被删除数据的后一个数据的迭代器,也就是pos
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
// 挪动数据
iterator cur = pos + 1;
while (cur != _finish)
{
*(cur - 1) = *cur;
++cur;
}
--_finish;
return pos;
}
测试:
实现了 insert 和 erase,就可以复用代码来实现 push_back 和 pop_back
void push_back(const T& val)
{
insert(end(), val);
}
void pop_back()
{
erase(end() - 1);
}
swap
两个vector对象的交换,如果调用算法库的swap,就是这样的:
template <class T> void swap ( T& a, T& b )
{
T c(a); a=b; b=c;
}
先拷贝构造一个临时对象c,又经历了两次赋值拷贝,一共经历了3次拷贝
如果T是比较复杂的自定义类型,3次拷贝消耗有点大,没有必要。我们只需要交换两个对象内部的指针,就可以达到交换数据的目的
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
测试:
完善构造函数
除了无参构造,我们还有几个构造函数没写
接口 | 接口说明 |
---|---|
vector() | 无参构造 |
vector(size_type n, const value_type& val = value_type ()) | 构造并初始化 n 个 val |
vector(const vector& v) | 拷贝构造 |
vector(InputIterator first, InputIterator last) | 使用迭代器进行初始化构造 |
迭代器区间构造
vector(InputIterator first, InputIterator last)
先来看最难的,其实也不难,就是之前没用过,有点陌生而已
我们可以使用另一个对象的迭代器区间进行构造,例如使用string对象的迭代器区间
- 只需遍历取出区间中的元素,然后调用vector对象的尾插即可
- 迭代器可能是string的迭代器,也可能是vector或者其他容器的,所以这里要使用函数模板
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
测试:
n value
vector(size_t n, const T& val = T());
构造并初始化 n 个 val
- 调用 n 次尾插即可
vector(size_t n, const T& val = T())
{
for (int i = 0; i < n; i++)
push_back(val);
}
测试:
程序编译错误,发现和 InputIterator 有关,为什么我们调用 n-val 的构造,会和迭代器区间构造有关系呢?——因为参数类型导致编译器调用的是函数模板
- 创建对象时,传的参数是(10, 1),参数类型是 int int
- n-val 构造函数的参数类型是 size_t T
- 迭代器构造函数模板的参数则是 InputIterator InputIterator
在编译器看来,迭代器函数模板的参数可以推演成 int int,更加匹配,所以就调用函数模板
我们可以把第一个参数转换为 size_t 类型,再测试一下:
运行成功,此时我们传的参数类型是 size_t int,和 n-val 函数的参数类型更加匹配。但是我们不能每次都这样传参数吧,太麻烦了
解决办法就是重载一个参数类型是 int int 的 n-val 构造函数
vector(size_t n, const T& val = T())
{
for (int i = 0; i < n; i++)
push_back(val);
}
vector(int n, const T& val = T())
{
for (int i = 0; i < n; i++)
push_back(val);
}
测试:
拷贝构造
vector(const vector& v)
用已存在的对象来构造一个新的对象
这里就直接使用现代写法了
- 现代写法:不用我们手动开空间然后拷贝数据。直接将 v 的数据尾插到当前对象
vector(const vector<T>& v)
{
reserve(v.capacity());
for (auto& e : v)
push_back(e);
}
测试:
析构函数
差点就把析构函数就给忘了
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
赋值拷贝 operator=
vector& operator= (const vector& x);
将已存在的对象赋值给另一个已存在对象
这里就不按照上面的函数头写了,使用现代写法
- 传值传参,也就是拷贝构造一个临时对象v
- 调用swap,将 *this 与 v 的数据进行交换
vector<T>& operator= (vector<T> v)
{
swap(v);
return *this;
}
测试:
memcpy的浅拷贝问题
我们在reserve的实现中,拷贝数据使用的是 memcpy 函数,它使用的是浅拷贝。如果vector中存的是自定义类型,且自定义类型会申请空间的话,那么析构时就会出现问题
例如使用string类型
目前来看代码可以正常运行,但是一旦扩容就会出现问题
以下是扩容之前的空间和扩容之后的空间,memcpy只是将旧空间中的数据拷贝到新空间,并不会把string指向的字符串也拷贝过来,这时旧空间和新空间的string对象指向同一块空间
扩容完毕后,我们需要调用 delete[] 释放旧空间,以下是delete[]的过程:
- 在 free 旧空间之前,会调用string对象的析构函数,释放string对象申请的空间
- 之后再调用 free 释放vector对象的空间
也就是说,当我们 delete[] 旧空间后,新空间中string指向的空间也已经被释放了
当调用 v1 的析构函数时,实际上string对象的空间已经被释放,会发生野指针问题
解决方法就是不用memcpy拷贝数据,直接遍历新开的空间和旧空间,将旧空间的数据赋值给新空间。如果数据是自定义类型,就会调用自定义类型的赋值拷贝,默认是深拷贝
void reserve(size_t n)
{
if (n > capacity())
{
size_t old_size = size();
// 申请新空间,拷贝旧数据
T* tmp = new T[n];
// memcpy(tmp, _start, sizeof(T) * size());
for (int i = 0; i < old_size; i++)
tmp[i] = _start[i];
delete[] _start;
// 更新数据
_start = tmp;
_finish = tmp + old_size;
_endofstorage = tmp + n;
}
}
测试:
迭代器失效
假设有一个迭代器pos,当我们在pos位置插入或者删除数据后,pos就会失效
- 如果插入数据时发生了扩容,那么迭代器 pos 指向的空间就会被释放掉,此时 pos 就不可以再使用了
- 虽然删除数据不会发生空间的改变,但是 pos 位置的数据被删除,我们不能再通过 pos 得到原来的数据了,这也是迭代器失效
使用失效的迭代器是有风险的,有什么方法可以规避呢?
- 迭代器失效后,就不要再使用了
- 如果一定要使用失效的迭代器,那就更新迭代器后再使用
如何更新迭代器呢?
- insert 插入数据成功后,会返回新插入的第一个数据的迭代器
- erase 删除数据后,会返回被删除数据的后一个位置的迭代器
在插入或者删除数据后,及时接收 insert 和 erase 的返回值来更新迭代器
代码
#pragma once
#include <iostream>
#include <assert.h>
#include <vector>
#include <string>
using namespace std;
namespace ns1
{
template <class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector() {}
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
vector(size_t n, const T& val = T())
{
for (int i = 0; i < n; i++)
push_back(val);
}
vector(int n, const T& val = T())
{
for (int i = 0; i < n; i++)
push_back(val);
}
vector(const vector<T>& v)
{
reserve(v.capacity());
for (auto& e : v)
push_back(e);
}
vector<T>& operator= (vector<T> v)
{
swap(v);
return *this;
}
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 old_size = size();
// 申请新空间,拷贝旧数据
T* tmp = new T[n];
// memcpy(tmp, _start, sizeof(T) * size());
for (int i = 0; i < old_size; i++)
tmp[i] = _start[i];
delete[] _start;
// 更新数据
_start = tmp;
_finish = tmp + old_size;
_endofstorage = tmp + 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 n)
{
assert(n >= 0);
assert(n < size());
return _start[n];
}
const T& operator[](size_t n) const
{
assert(n >= 0);
assert(n < size());
return _start[n];
}
iterator begin()
{
return _start;
}
const_iterator begin() const
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator end() const
{
return _finish;
}
void push_back(const T& val)
{
检查扩容
//if (_finish == _endofstorage)
//{
// size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
// reserve(newcapacity);
//}
插入数据
//*_finish = val;
//++_finish;
insert(end(), val);
}
void pop_back()
{
//assert(!empty());
//--_finish;
erase(end() - 1);
}
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
// 检查扩容
if (_finish == _endofstorage)
{
// pos相对位置
size_t len = pos - _start;
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
// 更新pos
pos = _start + len;
}
// 挪动数据
iterator cur = end();
while (cur > pos)
{
*cur = *(cur - 1);
--cur;
}
// 更新数据
*pos = val;
++_finish;
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
// 挪动数据
iterator cur = pos + 1;
while (cur != _finish)
{
*(cur - 1) = *cur;
++cur;
}
--_finish;
return pos;
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endofstorage = nullptr;
};
template <class T>
void PrintVector(const vector<T>& v)
{
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
cout << endl;
}
void test1()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
PrintVector(v1);
}
void test2()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
PrintVector(v1);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
// 填充
v1.resize(20);
PrintVector(v1);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
// 删除
v1.resize(2);
PrintVector(v1);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
}
void test3()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
// 迭代器遍历
vector<int>::iterator it1 = v1.begin();
for (; it1 != v1.end(); it1++)
cout << *it1 << " ";
cout << endl;
// 范围 for
for (auto& e : v1)
cout << e << " ";
cout << endl;
}
void test4()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
PrintVector(v1);
v1.pop_back();
PrintVector(v1);
v1.pop_back();
PrintVector(v1);
v1.pop_back();
PrintVector(v1);
v1.pop_back();
PrintVector(v1);
v1.pop_back();
PrintVector(v1);
}
void test5()
{
vector<int> v1;
v1.push_back(1);
PrintVector(v1);
// 头插
v1.insert(v1.begin(), 10);
PrintVector(v1);
// 尾插
v1.insert(v1.end(), 30);
PrintVector(v1);
// 中间插
v1.insert(v1.begin() + 1, 20);
PrintVector(v1);
// 尾插
v1.insert(v1.end(), 100);
PrintVector(v1);
// 尾插
v1.insert(v1.end(), 100);
PrintVector(v1);
// 尾插
v1.insert(v1.end(), 100);
PrintVector(v1);
}
void test6()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
PrintVector(v1);
// 头删
v1.erase(v1.begin());
PrintVector(v1);
// 尾删
v1.erase(v1.end()-1);
PrintVector(v1);
// 中间删
v1.erase(v1.begin()+1);
PrintVector(v1);
}
void test7()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
PrintVector(v1);
vector<int> v2;
v2.push_back(5);
v2.push_back(6);
v2.push_back(7);
v2.push_back(8);
PrintVector(v2);
v1.swap(v2);
PrintVector(v1);
PrintVector(v2);
}
void test8()
{
string s1 = "abcd";
vector<char> v1(s1.begin(), s1.end());
PrintVector(v1);
}
void test9()
{
/*vector<int> v1(10, 1);
vector<int> v2(v1);*/
vector<int> v1(10, 1);
vector<int> v2(10, 2);
v1 = v2;
PrintVector(v1);
PrintVector(v2);
}
void test10()
{
vector<string> v1;
v1.push_back("一");
v1.push_back("二");
v1.push_back("三");
v1.push_back("四");
v1.push_back("五");
PrintVector(v1);
}
}
);
}
void test7()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
PrintVector(v1);
vector<int> v2;
v2.push_back(5);
v2.push_back(6);
v2.push_back(7);
v2.push_back(8);
PrintVector(v2);
v1.swap(v2);
PrintVector(v1);
PrintVector(v2);
}
void test8()
{
string s1 = "abcd";
vector<char> v1(s1.begin(), s1.end());
PrintVector(v1);
}
void test9()
{
/*vector<int> v1(10, 1);
vector<int> v2(v1);*/
vector<int> v1(10, 1);
vector<int> v2(10, 2);
v1 = v2;
PrintVector(v1);
PrintVector(v2);
}
void test10()
{
vector<string> v1;
v1.push_back("一");
v1.push_back("二");
v1.push_back("三");
v1.push_back("四");
v1.push_back("五");
PrintVector(v1);
}
}