目录

一,vector类型简单介绍

1.简介

1.1.STL

1.2.vector

2.vector有哪些函数?

二,泛型

1.什么是泛型

2.泛型的实现

2.1.函数模板

2.2.类模板

三,相关函数写法

3.1.构造函数

3.2.增加函数

3.2.1.push_back

3.2.2.insert

一,vector类型简单介绍

1.简介

1.1.STL

STL是一个C++自带的一个数据结构的类,包括栈,队列,字符串等功能。这些数据结构统称为容器(containers)。

1.2.vector

vector(向量),是一种经常被使用的容器。它可以看做是一种动态大小的数组,并且可以存放任何类型的数据(即泛型)。

这里给大家一个使用的例子:

#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> v;
for(int i=0;i<10;i++){
v.push_back(i);
cout<<v[i]<<endl;
}
return 0;
}


2.vector有哪些函数?

1.构造函数
vector()//创建一个空vector
vector(int nSize)//创建一个vector,元素个数为nSize
vector(int nSize,const t& t)//创建一个vector,元素个数为nSize,且值均为t
vector(const vector&)//复制构造函数
vector(begin,end)//复制[begin,end)区间内另一个数组的元素到vector中
2.增加函数
void push_back(const T& x)//向量尾部增加一个元素X
iterator insert(iterator it,const T& x)//向量中迭代器指向元素前增加一个元素x
iterator insert(iterator it,int n,const T& x)//向量中迭代器指向元素前增加n个相同的元素x
iterator insert(iterator it,const_iterator first,const_iterator last)//向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据
3.删除函数
iterator erase(iterator it)//删除向量中迭代器指向元素
iterator erase(iterator first,iterator last)//删除向量中[first,last)中元素
void pop_back()//删除向量中最后一个元素
void clear()//清空向量中所有元素
4.遍历函数
reference at(int pos)//返回pos位置元素的引用
reference front()//返回首元素的引用
reference back()//返回尾元素的引用
iterator begin()//返回向量头指针,指向第一个元素
iterator end()//返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin()//反向迭代器,指向最后一个元素
reverse_iterator rend()//反向迭代器,指向第一个元素之前的位置
5.判断函数
bool empty() const//判断向量是否为空,若为空,则向量中无元素
6.大小函数
int size() const//返回向量中元素的个数
int capacity() const//返回当前向量所能容纳的最大元素值
int max_size() const//返回最大可允许的vector元素数量值
7.其他函数
void swap(vector&)//交换两个同类型向量的数据
void assign(int n,const T& x)//设置向量中前n个元素的值为x
void assign(const_iterator first,const_iterator last)//向量中[first,last)中元素设置成当前向量元素


二,泛型

1.什么是泛型?

最简单的例子:

#include<iostream>
#include<vector>
using namespace std;
int main(){
int a=5,b=10;cout<<max(a,b);
float c=3.14,d=9.99;cout<<max(c,d);
char e='x',f='*';cout<<max(e,f);
}


这里,max函数即可以处理int,又可以处理float,char类型的最大值。这就是泛型。max函数无论针对哪一个类型,操作都是相同的。因此,我们使用通用类型,让函数不关注类型只关注具体的操作。

有人会问,其实使用函数重载不就能完成了吗?(不了解函数重载是什么的,请看我前面写过的《自制string类型》,里面有说明)但是,函数重载要重复写好几次,不方便。

2.泛型的实现

2.1.函数模板

在写真正的vector泛型之前,我们首先使用MAX函数练习一下,借此看看如何写泛型。

#include<iostream>
#include<vector>
using namespace std;
template<typename T>
T MAX(T a,T b){
if(a>b)return a;
else return b;
}
int main(){
int a=5,b=10;cout<<MAX(a,b);
float c=3.14,d=9.99;cout<<MAX(c,d);
char e='x',f='*';cout<<MAX(e,f);
}


template表示定义一个叫做T的类型,这个类型是一个通用类型。这个语句告诉编译器,要开始泛型编程,其中T是要使用的泛型类型。

执行的时候,编译器会自动根据参数的类型,把T转换为int,float等类型,进行计算。

注意,泛型的函数不会进行自动类型转换,例如​​cout<<MAX('a',100);​​这个语句,如果使用的是泛型类型,会编译错误,但是使用普通类型不会报错,因为普通类型的函数会进行自动类型转换。

2.2.类模板

写法和函数模板非常类似。

template<typename T>
class Vector{
T *numbers;

};


在声明Vector类型的时候,需要注意:声明不能写成:

Vector v;


而必须写成:

Vector<int>v;


这样才可以告诉编译器需要定义的类型。

三,相关函数写法

3.1.构造函数

终于来到写函数的时候了。第一个写的函数一定是构造函数。我们知道,vector其实是一个“动态数组”,而动态数组的实现需要依靠指针。我们使用一个变量n来记录目前vector内部的元素个数。

最开始的时候,整个向量内部为空,那么n为0,并且无法存放数据。在放入数据的时候,向量会申请空间,来放入数据。

四个构造函数:

class Vector{
T *numbers;
int n;
Vector(){
n=0;
}
Vector(int nsize){//初始化大小
n=nsize;
numbers=(T*)malloc(nsize*sizeof(T));
}
Vector(int nsize,T t){//初始化大小并放入nsize个t
n=nsize;
numbers=(T*)malloc(nsize*sizeof(T));
for(int i=0;i<nsize;i++)numbers[i]=t;
}
Vector(Vector <int> &v){//复制另一个vector的东西
n=v.n;
numbers=(T*)malloc(n*sizeof(T));
for(int i=0;i<nsize;i++)numbers[i]=v.numbers[i];
}
};


为什么使用malloc不用new?因为malloc出来的内存可以realloc,而new的不行。代码很简单,就不多说了。

3.2.增加函数

3.2.1.push_back

功能:往最后一个元素的后面再加一个元素

void push_back(T x){
n++;
if(n==1)numbers=(T*)malloc(n*sizeof(T));
else numbers=(T*)realloc(numbers,n*sizeof(T));
T[n-1]=x;
}


如果只有一个元素,那么执行Malloc。如果已经有元素了,那么执行realloc。

但是,不断的realloc是非常慢的。下面内容引用自《征服C指针》:

我们经常需要对数组顺次追加元素,这种情况下,如果每追加一个元素都利用 realloc()进

行内存区域扩展,将会发生什么呢?

如果手气不错,后面正好有足够大的空地儿,这样还好。如果不是这样,就需要频繁地复制

区域中的内容,这样自然会影响运行效率。另外,不断地对内存进行分配、释放的操作,也

会引起内存碎片化。

最好的做法是一次性多分配一些空间(例如100个sizeof(T))。但是这样就要多在类里面加一个变量,函数也会变得很复杂。我们还是考虑把realloc封装起来吧。

class Vector{
T *numbers;
int n;//大小
int memory;//已经分配的内存

T* Allocate(T *mem,int newsize){
n=newsize;
if(memory>=n)return NULL;
else memory=newsize+100,return realloc(mem,newsize+100);
}


如果返回NULL,说明内存已经足够,否则就返回realloc的新的地址。

void push_back(T x){
n++;
if(n==1)numbers=(T*)malloc(n*sizeof(T));
else if (Allocate(numbers,n*sizeof(T))!=NULL)numbers=Allocate(numbers,n*sizeof(T));
numbers[n-1]=x;
}


上面是修改的push_back示例。这样就方便多了。

当然,为了实现起来方便,本文之后的所有例程都写作realloc的形式,便于理解和阅读。

3.2.2.insert函数

insert的作用是插入,我们目前是使用数组进行存储的,因此,插入和删除需要移动元素(顺序表,如果是链表则没有这个问题)

为了制造起来方便,我们这样规定:vector插入的东西必须是一个vector中的东西或者数组。至于什么迭代器iterartor,就不做了。

同时,为了初始化方便,我们制作一个可变长函数用于初始化元素,如下:

Vector(数组长度n,元素1,元素2,...,元素n);

Vector(int nsize,...){
n=nsize;
numbers=(T*)malloc(nsize*sizeof(T));
va_list ap;
va_start(ap,nsize);
for(int i=0;i<n;i++)numbers[i]=va_arg(ap,T);
va_end(ap);
}


可变长函数的基本格式:

函数名 (参数,...){
va_list ap;
va_start(ap,[省略号前最后一个元素]);
for(int i=0;i<[个数];i++){
...va_arg(ap,[参数类型]);//用于取得省略的参数
}
va_end(ap);
}