文章目录
- vector介绍
- vector和string的区别
- 补充知识
- initializer_list
- emplace_back
- 结构化绑定
- vector的使用
- 构造
- 析构
- 遍历+修改
- insert
- find
- 流插入/流提取
- vector\<vector>(杨辉三角)
- vector模拟实现
- 浅品STL源码
- 构造函数
- 拷贝构造
- 多参数构造
- 迭代器区间构造
- n个val初始化
- swap
- operator=
- reserve
- size
- capacity
- push_back(补充解释源码里为什么要用定位new)
- 迭代器
- pop_back
- insert(补充迭代器失效)
- erase
- clear
- resize
- 深层次的深拷贝问题
- 源码
vector介绍
vector底层和string一样也是数组,类似数据结构我们介绍的顺序表。
它和string的区别是string的元素类型只能是字符类型,vector有模板可以是任何类型,包括自定义类型。
vector和string的区别
既然vector和string底层都是数组,那存char类型数据的vector<char>和string有什么区别呢?
1、虽然vector也有date接口返回底层数组首元素的指针,但是vector<char>末尾没有\0,无法兼容c语言。
2、vector插入数据只有push_back插入一个数据,find也只能找一个数据,但是string有字符串的概念,所以它有append,operator+=插入字符串,find也能找子串。
补充知识
initializer_list
- 这是C++11引入的新语法,initializer_list是一种类型,使用它需要包<initializer_list>头文件。
- 我们把一些数据用大括号括起来一起赋值给i1,这个i1类型就是initializer_list。
它是底层在栈上开了一块空间,把常量数组存起来,有一个指针指向开始,有一个指针指向结束,所以它支持迭代器,有size()和范围for,后面讲vector多参数构造的底层实现会用到。
用法:
- typid()可以打印一个对象或者类型的真实类型。
- initializer_list支持迭代器,所以可以用迭代器遍历。
用途:
- C++11要引入这个新语法是一是为了支持用任意多个值直接构造初始化容器,操作如下:
vector<int> v1({ 10, 20, 30 });vector<double> v2({ 10.1, 20.4, 30.7, 40.6, 50.5 });
这就是直接调用构造函数。
但是我们实际更喜欢用下面这种操作:
vector<int> v1 = { 10, 20, 30 };vector<double> v2 = { 10.1, 20.4, 30.7, 40.6, 50.5 };
这里会隐式类型转换,initializer_list会先构造一个vector,再拷贝构造给给对象,编译器会优化为直接构造。
- 二是可以用于多参数构造函数的隐式类型转换:
struct A{A(int a1, int a2):_a1(a1),_a2(a2){}int _a1;int _a2;};vector<A> v1;A aa1(1, 1);//有名对象v1.push_back(aa1);//匿名对象v1.push_back(A(2, 3));//多参数隐式类型转换v1.push_back({4,5});
emplace_back
这里小编只能介绍它的用法,理解他还需要后面的知识。
它是模板的可变参数,在上面多参数隐式类型转换的时候像下面这样用,效率会更高。
A1.emplace_back(4,5);
结构化绑定
auto [x, y] = aa1;for (auto [X, Y] : v1){cout << X << ':' << Y << endl;}
当对象是一个结构对象比如结构体体时,可以用上面这种方法访问对象的成员。
vector的使用
这里小编不会一一介绍,和string基本一样的接口就跳过了,和string有区别的会挑出来介绍。
构造
//默认构造vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);//10个1构造vector<int> v2(10, 1);//迭代器区间构造vector<int> v3(v2.begin(), v2.end())
析构
~vector(){if (_first){delete[] _first;_first = _finish = _endofstorage = nullptr;}}
_first不为空再delete,delete后记得把指针置空。
遍历+修改
//使用size()for (int i = 0; i < v1.size(); i++){cout << v1[i] << " ";}cout << endl;//范围forfor (auto e : v2){cout << e << " ";}cout << endl;//迭代器vector<int>::iterator it = v3.begin();while (it != v3.end()){cout << (*it) << " ";it++;}cout << endl;
insert
vector的插入重载要比string少很多,并且vector是以迭代器为标准插入的,string是size_t pos为标准。
//头插v1.insert(v1.begin(), 1000);//下标为3的位置前面插入v1.insert(v1.begin() + 3, 1000);
find
- vector并没有自己的find,它用的是算法库的find,它是在一段迭代器区间查找。
- string要自己实现find是因为string需要从某一位置开始向后查找还要查找子串。
算法库的find是通过一段迭代器区间找,因为迭代器区间是左闭右开的,所以找到了就返回第一个找到的元素的迭代器,没找到就返回last。
auto it = find(v1.begin(), v1.end(), 10);if (it != v1.end()){v1.insert(it, 100);}
流插入/流提取
- vector以及后面的容器都不再提过输入输出,要输入输出自己通过范围for或者迭代器实现。
- string提供因为会经常输入输出字符串。
vector<vector>(杨辉三角)
在介绍vector<vector>之前我们先看一道题:
题目链接:杨辉三角
这道题就需要用到vector<vector>来实现C语言类似二维数组的功能。
要理解vector我们先理解它是如何实例化的,我们以vector<vector<int>>为例:
这里和C语言单纯的二维数组不同,vector<vector>是对象数组。
当我们要读写vector<vector<int>>需要采用和C语言一样的方法,但是底层原理已经完全不同了,C语言的指针的偏移和解引用,这里是两个函数的调用,下面代码两种写法是等价的,第一个是vector<vector>里的operator[],返回值的vector<int>&,返回值是可以修改的,第二个是vector<int>里的operator[],返回值是int&。有些读者可能会有疑问,两个函数调用不会拉低效率吗?内联函数的作用就体现出来了,operator[]很短小,所以会直接内联展开替换成指令,不会调用函数。
vv[i][j];
vv.operator[](i).operator[](j);
完整代码:
class Solution {
public:vector<vector<int>> generate(int numRows) {//定义vector<vector>+开空间vector<vector<int>> vv;//开numRows行个空间,resize第二个参数要传对象,这里是匿名对象vv.resize(numRows, vector<int>());for(int i = 0; i < numRows; i++){//每一列数据个数是列数加1vv[i].resize(i + 1, 1);}//控制行//前两行不用动,i从2开始for(int i = 2; i < vv.size(); i++){//控制列//每列的第一个和最后一个不用动for(int j = 1; j < vv[i].size() - 1; j++){vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];}}return vv;}
};
vector模拟实现
首先我们要明确这里我们只用两个文件来实现,因为vector要严格用模板来实现,模板的声明和定义不能分离到两个文件。
浅品STL源码
在自己模拟实现之前,先看一下它的源码吧,但是小编目前功力不足,只能带大家看一部分。 首先看它的成员函数:
iterator start;
iterator finish;
iterator end_of_storage;
嗯?三个迭代器?这和string完全不一样啊,快去看看这些迭代器是什么:
typedef T value_type;
typedef value_type* iterator;
哦,原来就是原始指针啊,那就猜一下start就是指向数组首元素的指针,finish是指向有效数据结尾下一个位置的指针,end_of_storage是指向空间末尾的指针,那事实是不是这样呢,接下来应该去找begin()和end()和capacity():
iterator begin() { return start; }
iterator end() { return finish; }
size_type capacity() const { return size_type(end_of_storage - begin()); }
看来猜的大差不差,接下来就开始模拟实现vector吧。
构造函数
vector()
{ }
拷贝构造
vector(const vector<T>& v){reserve(v.capacity());for (auto e : v){push_back(e);}}
拷贝构造本质就是一个对象想要另一个对象一样大的空间一样的值,所以可以复用reserve来开空间,再用范围for遍历把数据全部拷贝过来。
但是这里要注意一个细节,在拷贝构造之前需要把成员变量初始化,如果不初始化的话在有些编译器里就会默认是随机值,然后capacity()来调用这些随机值就会出错,这里我们初始化方法推荐在成员变量处给随机值。
那既然我们已经给了缺省值了,为什么前面还要实现一个默认构造函数呢,我们不写它直接用缺省值不就行了吗?其实就算它没有实质用处,也还是要写它,构造函数规定只要显示写了构造函数那么编译器就不会生成默认构造了,我们写了的拷贝构造就是构造函数,所以如果不写前面那个默认构造函数的话我们就无法调用默认构造函数了。
除了自己写之外我们还可以强制编译器生成默认构造:
vector() = default;
多参数构造
vector(initializer_list<T> i1){reserve(i1.size());for (auto e : i1){push_back(e);}}
这里就可以用前面介绍的initializer_list来实现多参数构造。
迭代器区间构造
这里我们可以用函数模板,如果我们写死为iterator的吗那只有vector能调用这里的迭代器区间构造,如果我们写成模板那其他类对象比如string的迭代器也能构造初始化,但是要数据类型匹配,比如字符就可以转化成这里vector实例化出的整型。
也就是说类模板的成员函数还可以继续写成函数模板。
template <class InputIterator>vector(InputIterator first, InputIterator last){reserve(last - first);while (first != last){push_back(*first);first++;}}
n个val初始化
这个构造用的也挺多,比如前面讲的杨辉三角。
这里还要注意参数匹配,不匹配的话会调到迭代器区间初始化去。
vector(int n, const T& val = T()){reserve(n);resize(n, val);}
swap
void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}
operator=
vector<T>& operator=(vector<T>& v){swap(v);return *this;}
这里就是我们在string部分介绍的现代写法。
reserve
void reserve(size_t n){//若外部调用该函数需要判断if (n > capacity()){size_t old_size = size();//开新空间T* tmp = new T[n];//拷贝旧空间数据到新空间,并释放旧空间//若_start为0(nullptr),无需拷贝if (_start){//memcpy会引发深层次的深拷贝问题//memcpy(tmp, _start, sizeof(T) * old_size);for (int i = 0; i < old_size; i++){tmp[i] = _first[i];}delete[] _start;}_start = tmp;_finish = _start + old_size;_endofstorage = _start + n;}}
这里要提前把旧对象size()值保存下来,避免后面调用size() {return _finish - _first;}其中一个是旧的数据,一个是更新后的数据,结果不是我们想要的。
注意拷贝数据时不能用nencpy,详细解释见深层次的深拷贝问题。
size
//不改变调用对象的函数加const
size_t size() const
{return _finish - _first;
}
capacity
size_t capacity() const
{return _endofstorage - _first;
}
push_back(补充解释源码里为什么要用定位new)
void push_back(const T& x)
{if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;finish++;
}
这里我们直接就把x的值赋给finish所在位置了,因为我申请的空间是直接new出来的,不仅开辟了空间,还调用了构造函数初始化这块空间,所以不管x是内置类型还是自定义类型,都可以直接赋值。
但是源码里的vector空间不是直接在堆上new的,而是在内存池里malloc的空间,所以空间是没有初始化的,若空间里是自定义类型的变量,直接赋值就会出问题:
我们以string为例,先看我们实现的string的赋值运算符重载:
string& string::operator=(const string& s) {if (*this != s){char* tmp = new char[s._size + 1];memcpy(tmp, s._str, s._size + 1);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;}
这里我们用没初始化的s直接调用赋值运算符重载就会出问题,因为没初始化的s的成员变量都是随机值,比方说new char[s._size +
1]这里的_size就是随机值,那么它最终new了多少数据就是未知的,和我们的想法违背。
所以源码里还使用了定位new用来显示调用构造函数,避免出现上面的情况。
迭代器
typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _first;}iterator end(){return _finish;}const_iterator begin() const{return _first;}const_iterator end() const{return _finish;}
普通迭代器和const迭代器都要实现,以便const对象调用。
pop_back
bool empty(){return _finish == _first;}void pop_back(){assert(!empty());_finish--;}
pop_back不用删除数据,只用把_finish–就行了,但是要注意判断数组是否为空。
insert(补充迭代器失效)
iterator insert(iterator pos, const T& x){assert(pos >= _first && pos <= _finish);//扩容if (_finish == _endofstorage){size_t len = pos - _first;reserve(capacity() == 0 ? 4 : 2 * capacity());//更新pospos = _first + len;}//挪动数据iterator end = _finish;while (end >= pos){*(end + 1) = *end;end--;}//插入数据*pos = x;_finish++;return pos;}
这里返回值为什么是迭代器,在后面erase部分再展开讲解。
乍一看这段代码非常完美,其实潜藏了一个坑点,当数组空间不够要扩容时,因为是堆上的空间,所以大概率会异地扩,那么扩完之后_first和_finish都指向新空间了,但是pos还指向旧空间,pos不就成野指针了吗,其实这里说是野指针并不准确,官方来讲应该是迭代器失效,因为这里迭代器恰好就是原生指针,其他容器并不一定。
解决方法:只需要每次扩容的时候更新pos就行了,运用指针的相对位置,先用len记录pos到_first的距离,扩容后_first+len就得到更新后的pos了。
优化后:
void pop_back(){assert(!empty());_finish--;}void insert(iterator pos, const T& x){assert(pos >= _first && pos <= _finish);//扩容if (_finish == _endofstorage){size_t len = pos - _first;reserve(capacity() == 0 ? 4 : 2 * capacity());//更新pospos = _first + len;}//挪动数据iterator end = _finish;while (end >= pos){*(end + 1) = *end;end--;}//插入数据*pos = x;_finish++;}
我们再来看下面这段代码:
int x;cin >> x;auto it = find(v.begin(), v.end(), 4);if (it != v.end()){v.insert(it, x * 10);//it是否还能用?}
意思是我们随便输入一个x,然后再v里找是否有x,有的话在它前面插入10x。 这里我们想想插入过后迭代器it是否还能用?(因为标准没有规定何时扩容,所以这里一致认为insert后扩容了并且导致迭代器失效)
有的读者可能会认为当然可以啊,it的数据都更新了,但是其实是不行的,因为更新的只是形参pos,这里只是传值所以形参的改变不会影响实参。
那直接把insert的第一个参数类型改为iterator&不就行了吗,其实这也是不对的,比如下面:
v.insert(v.begin(), 30);
v.insert(it + 2, 30);
用begin函数调用insert,我们前面实现begin函数是传值返回的_first,所以返回的并不是_first本身,而是它的一份临时拷贝,那如果这里insert引用了这个返回值,就会发生权限的放大,第二钟情况也是同理,it + 2表达式的的结果也是临时对象,具有常性的。所以小编在这里提醒大家,纵使引用有万般好,也要视情况用。
erase
void erase(iterator pos){assert(pos >= _first && pos < _finish);auto it = pos + 1;//挪动数据while (it != _finish){*(it - 1) = *it;it++;}_finish--;}//优化后iterator erase(iterator pos){assert(pos >= _first && pos < _finish);auto it = pos + 1;//挪动数据while (it != _finish){*(it - 1) = *it;it++;}_finish--;return pos;}
实现思路就是把目标位置以后的数据往前挪,开头的assert也能检查数组是否为空。
那erase后迭代器是否会失效呢?我们来看下面这段程序:
std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);//删除偶数auto it = v.begin();while (it != v.end()){if ((*it) % 2 == 0){v.erase(it);//it是否失效?}it++;}
这段代码会删除v里面的的偶数数据。我们分析一下,第一次会删除2,然后后面数据依次往前挪,以前指向2的迭代器就被动指向3了,这里就出现迭起器失效的问题了,因为指向的内容被改变了,因为vs对迭代器失效进行了严格检验,所以迭代器失效后再对这个迭代器访问会直接报错,但是在g++下就能通过,因为不同编译器对迭代器失效的处理是不同的,总的来说,这里是有问题的。
并且这里还有一个隐藏问题,就是删除2了之后,所有数据往前挪,那此时迭代器就指向3了,it++后就指向4了,相当于3没判断直接跳过了。
解决方法标准已经给出了,我们来看标准库的erase:
它给了一个返回值iterator,它返回的是删除元素位置下一个位置的迭代器,这就是方便我们来赋值更新迭代器的,也就是用it接受这个返回值就达到了更新迭代器的目的,这样就。完美解决了迭代器失效的问题。
优化后:
auto it = v.begin();while (it != v.end()){if ((*it) % 2 == 0){it = v.erase(it);}else{it++;}}
总结:无论insert还是erase都会造成迭代器失效,因为它们有中间迭代器参数pos,解决办法就是迭代器失效后及时更新迭代器。
clear
void clear(){_finish = _start;}
这里只能把_start赋值给_finish,因为_start必须指向空间的开始,clear不用释放空间,交给析构函数来释放。
resize
在介绍resize之前,小编想先讲一下C++标准因为支持模板,所以它还支持了内置类型的构造函数,所以下面这些代码都是支持的:
int i = 10;int j(10);int k = int();int m = int(10);
接下的resize的缺省参数就会用到。
void resize(size_t n, T val = T()){if (n > size()){reserve(n);while (_finish != _start + n){(*_finish) = val;_finish++;}}else{_finish = _start + n;}}
这里resize第二个参数是模板,所以就不能像以前一样用0充当缺省值,这里最好就是用匿名对象T()当缺省值,(其实匿名对象就是调用了默认构造,因为默认构造就是不传实参也可以调用的构造)不管是内置类型还是自定义类型的默认构造的值都接近与0,int就是0,char就是ASCII码为0的’\0’,这样不管参数是内置类型还是自定义类型都可以调用。
深层次的深拷贝问题
wusaqi::vector<string> v1;v1.push_back("1111111111111111111111");v1.push_back("1111111111111111111111");v1.push_back("1111111111111111111111");v1.push_back("1111111111111111111111");print(v1);
这段代码是用string来实例化vector,我们分别来看vector扩容前和扩容后打印出来的值:
我们看到在扩容后数组的前四个vector被置成随机值了,敏锐一点的读者应该会感觉是浅拷贝之类的问题。事实确实是这样。我们既然知道是扩容后发生的问题,那我们来看扩容接口:
void reserve(size_t n){//若外部调用该函数需要判断if (n > capacity()){size_t old_size = size();//开新空间T* tmp = new T[n];//拷贝旧空间数据到新空间,并释放旧空间//若_start为0(nullptr),无需拷贝if (_start){memcpy(tmp, _start, sizeof(T) * old_size);delete[] _start;}_start = tmp;_finish = _start + old_size;_endofstorage = _start + n;}}
这里拷贝数据是memcpy,我们知道memcpy只会一个字节一个字节拷贝,相当于它会原封不动的把string的成员变量:_str _size _caoacity拷贝到tmp,也就是说tmp数组里的string成员变量_str和原数组里的string成员变量_str的值是一样的,指向同一块空间,那么调用delete释放原数组后tmp数组里的值也被释放了,所以就解释了为什么扩容后前四个string为随机值。
我们借用监视窗口可以看到底层数组确实是指向同一块空间:
这里问题就出在reserve的拷贝上,解决思路很简单,我们不用memcpy,而是for循环遍历把每一个数组里的元素然后用赋值运算符重载把元素依次赋给tmp数组,这样内置类型正常浅拷贝赋值,自定义类型就会去调用自己的赋值运算符重载完成深拷贝。
void reserve(size_t n){//若外部调用该函数需要判断if (n > capacity()){size_t old_size = size();//开新空间T* tmp = new T[n];//拷贝旧空间数据到新空间,并释放旧空间//若_start为0(nullptr),无需拷贝if (_start){//memcpy会引发深层次的深拷贝问题//memcpy(tmp, _start, sizeof(T) * old_size);for (int i = 0; i < old_size; i++){tmp[i] = _first[i];}delete[] _start;}_start = tmp;_finish = _start + old_size;_endofstorage = _start + n;}}
源码
vector.h
#pragma once
#include <assert.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;namespace wusaqi
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}vector(){}//vector() = default;vector(const vector<T>& v){reserve(v.capacity());for (auto e : v){push_back(e);}}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){swap(v);return *this;}vector(initializer_list<T> i1){reserve(i1.size());for (auto e : i1){push_back(e);}}template <class InputIterator>vector(InputIterator first, InputIterator last){reserve(last - first);while (first != last){push_back(*first);first++;}}vector(int n, const T& val = T()){reserve(n);resize(n, val);}vector(size_t n, const T& val = T()){reserve(n);resize(n, val);}~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}void reserve(size_t n){//若外部调用该函数需要判断if (n > capacity()){size_t old_size = size();//开新空间T* tmp = new T[n];//拷贝旧空间数据到新空间,并释放旧空间//若_start为0(nullptr),无需拷贝if (_start){//memcpy会引发深层次的深拷贝问题//memcpy(tmp, _start, sizeof(T) * old_size);for (int i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old_size;_endofstorage = _start + n;}}T& operator[](size_t i){//断言防止越界访问assert(i < size());return _start[i];}//不改变调用对象的函数加constsize_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void push_back(const T& x){//size_t storage = capacity();//if (_finish == _endofstorage)//{// size_t newstorage = storage == 0 ? 4 : 2 * storage;// reserve(newstorge);//}//简化:if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;}bool empty(){return _finish == _start;}void pop_back(){assert(!empty());_finish--;}iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);//扩容if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());//更新pospos = _start + len;}//挪动数据iterator end = _finish;while (end >= pos){*(end + 1) = *end;end--;}//插入数据*pos = x;_finish++;return pos;}iterator erase(iterator pos){assert(pos >= _start && pos < _finish);auto it = pos + 1;//挪动数据while (it != _finish){*(it - 1) = *it;it++;}_finish--;return pos;}void clear(){_start = _finish;}void resize(size_t n, const T& val = T()){if (n > size()){reserve(n);while (_finish != _start + n){(*_finish) = val;_finish++;}}else{_finish = _start + n;}//size_t capacity = _endofstorage - _start;//size_t size = _finish - _start;//if (n < size)//{// _finish = _start + n;//}//else//{// if (n > capacity)// {// reserve(n);// _endofstorage = _start + n;// }// iterator pos = _finish;// while (pos != _start + n)// {// (*pos) = val;// pos++;// }// _finish = _start + n;//}}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "vector.h"template<class T>
void print(const T& v)
{for (auto ch : v){cout << ch << " ";}cout << endl;
}void test01()
{wusaqi::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.pop_back();for (int i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;for (auto ch : v){cout << ch << " ";}cout << endl;print(v);
}void test02()
{wusaqi::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//v.push_back(5);v.insert(v.begin(), 30);print(v);//v.erase(v.begin() + 2);//print(v);int x;cin >> x;wusaqi::vector<int>::iterator it = find(v.begin(), v.end(), x);if (it != v.end()){v.erase(it);}print(v);}void test03()
{wusaqi::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(5);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(4);print(v);//删除偶数auto it = v.begin();while (it != v.end()){if ((*it) % 2 == 0){it = v.erase(it);}else{it++;}}print(v);v.clear();print(v);
}void test04()
{wusaqi::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(5);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(4);print(v);v.resize(10, 100);print(v);//int i = 10;//int j(10);//int k = int();//int m = int(10);
}void test05()
{wusaqi::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);print(v1);wusaqi::vector<int> v2(v1);print(v2);wusaqi::vector<int> v3 = { 1, 2, 3, 4, 5, 1, 1, 1 };print(v3);wusaqi::vector<int> v4(v3.begin() + 2, v3.end() - 2);print(v4);string s1("good morning");wusaqi::vector<int> v5(s1.begin() + 2, s1.end() - 2);print(v5);std::vector<int> v6(10, 1);print(v6);std::vector<size_t> v7(10, 1);print(v7);
}void test06()
{wusaqi::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);print(v1);wusaqi::vector<int> v3;v3 = v1;print(v3);
}void test07()
{wusaqi::vector<string> v1;v1.push_back("1111111111111111111111");v1.push_back("1111111111111111111111");v1.push_back("1111111111111111111111");v1.push_back("1111111111111111111111");v1.push_back("1111111111111111111111");print(v1);
}int main()
{//test01();//cout << typeid(std::vector<int>::iterator).name() << endl;//cout << typeid(wusaqi::vector<int>::iterator).name() << endl;//test02();//test03();//test04();test05();//test06();//test07();return 0;
}
以上就是小编分享的全部内容了,如果觉得不错还请留下免费的关注和收藏
如果有建议欢迎通过评论区或私信留言,感谢您的大力支持。
一键三连好运连连哦~~