【C++】vector
【C++】vector
- vector简介
- vector类的常用接口
- 构造
- 迭代器
- Capacity
- 增删查改
- vector类的模拟实现
- 模板类
- 成员变量
- 搭一个框架
- 无参构造
- size && capacity
- operator[]
- push_back()
- Capacity
- size && capacity
- empty
- reserve
- resize
- 迭代器
- begin()
- end()
- 增删查改
- push_back
- pop_back
- insert
- erase
- swap
- 完善构造函数
- 迭代器区间构造
- n value
- 拷贝构造
- 析构函数
- 赋值拷贝 operator=
- memcpy的浅拷贝问题
- 迭代器失效
- 代码
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 valvector<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);// 更新pospos = _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);// 更新pospos = _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;// 范围 forfor (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);}
}