上一讲我们说了一些类型的typedef,常规迭代器和反向迭代器,至少我们现在知道iterator和reverse_iterator的区别和联系,现在我们继续往下探究,看看vector还有些什么事值得挖究的。
不管是数组还是一个集合,关于里面的元素通常我们都是可以访问的,vector照样不例外,他提供了一组操作来访问序列中的元素,当然,大家肯定想到了,至少想起了一个来:我们常说的[]操作符重载,通常来说,我们获取了迭代器也就等于获取了访问元素的区别,但是对于C程序员来说,下标是再顺眼不过的了,所以光获取迭代器还不足够我们对元素的访问,我们至少还要能够通过下标去访问元素,所以,我们就得重载[]操作符。
———————————————–
template<typename T,typaname A=allocator<T>>
class vector{
public:
//
//
reference operator[](size_type n);
const_reference operator[](sieze_type n) const;
reference at(size_type n);
const_reference at(size_type n) const;
//
//
};
——————————————-
关于下标操作,vector提供了两种方式,一种就是我们常规的operator[],但是这种方式是不对范围检查的,也就是说,就算我们的vector里的元素只有50个,你照样可以写出vector[100]来,而不影响编译,当然一旦运行会得到一个响亮的耳光,但at不同,因为at是对边界进行检查了,所以,当下标超出边界后,at会抛出一个out_of_range的异常。像下面:
——————————————–
void f(vector<int>&
c){
try{
cout<<c[20]<<endl;
//1
cout<<c.at(20)<<endl;
//2
}catch(out_of_range){
cout<<"
out_of_range"
<<endl;
}
}
————————————————
对于我们上面的用法,如果我们用的是注释为1的那句来获取第20给元素的话,程序会直接宕掉(将设我们c里面没有21个元素的话),但是如果我们不容1而选择2的话,就算c里面没有21给元素,程序也可以正常运行,因为抛出的异常会被我们的try……catch……捕获,让我们知道发生了什么。
当然,通常情况下,我们一般都不会这么莽莽撞撞的写程序,一个好的习惯,当我们需要访问元素的时候我们一般都会自觉的检查一下边界:
—————————
void f(vector<int>&
c){
for(int i=0;
i<c.size();
i++)
c[i]=XX;
}
—————————–
所以,通常我们见到的都会是上面的这样,因为在for循环里面就对边界进行了检查,所以使用[]是安全的。
同样,vector还提供了一组堆栈操作,在谈堆栈操作之前,我们要明白什么叫堆栈操作,其实就是我们通常说的先进后出的一种操作方式,还记得我们在迭代器那章谈到的两个操作吗?其实那就是一组堆栈操作:
—————————–
template<typename T,typaname A=allocator<T>>
class vector{
public:
//
//
void push_back(const T&
t);
void pop_back();
reference back();
const_reference back() const;
//
//
};
——————————-
push_back是向末尾插入一个元素,pop_back却是弹出一个元素,说确切点是删除一个末尾的元素,通常情况下,当我们使用pop_back后我们不知道弹出来的是什么,当然,如果我们想要知道是什么的话,我们得自己去查看,那么该怎么去查看呢?嗯,就是我们上面的back()成员函数了,这个函数返回容器的最后一个元素:
——————————
reference back(){
return *(end()-1);
}
——————————
除了堆栈操作外,vector还有一些表操作,他不但可以从末端的插入和删除,还可以像list一样从任意位置插入和删除。
——————————–
template<typename T,typaname A=allocator<T>>
class vector{
public:
//
//
iterator insert(iterator pos,const T&
t);
//1
void insert(iterator pos,size_type n,const T&
t);
//2
template<typename In>
void insert(iterator pos,In first,In last);
//3
iterator erase(iterator pos);
//4
iterator erase(iterator first,iterator last);
//5
void clear();
//6
//
//
};
——————————-
这组操作几乎可以带起list,但是说实在的,无法代替,因为如果我们想要用vector去插入删除(不是末端)的话,效率低得吓人,现在我们来看看这几个函数,按照我们注释的顺序来说吧:
——————————
1)这个函数,insert在pos的位置前面插入t,然后返回t的迭代器。
2)这个函数,在pos的前面插入n个t,同时返回最后一个t的迭代器。
3)这个函数,在pos的前面插入另一个容器,同时返回插入这个序列的最后一个元素的迭代器。
4)这个函数的功能是删除pos的元素,然后返回指向下一个元素的迭代器。
5)看样子就知道他想要干嘛,删除一系列元素,但是不包括last,同时返回指向最后一个元素的迭代器。
6)我想不用多说了,直接一下子清空所有元素。
—————————–
我们来看看一些实际点的例子:
—————————–
int main()
{
int b[] ={0,6,7,8,9 };
vector<int>a(b,b+5);
vector<int>::iterator p=a.begin();
a.insert(p+1,1);
p = a.begin();
cout<<"
————————"
<<endl;
cout<<"
insert 1 at pos = 1 :"
<<endl;
while(p != a.end())
cout<<*p++<<endl;
int c[] ={2,3,4,5 };
p = a.begin();
a.insert(p+2,c,c+4);
p = a.begin();
cout<<"
————————"
<<endl;
cout<<"
insert 2 3 4 5 at pos = 2:"
<<endl;
while(p != a.end())
cout<<*p++<<endl;
p = a.begin();
a.erase(p+2);
p = a.begin();
cout<<"
———————–"
<<endl;
cout<<"
erase pos = 2:"
<<endl;
while(p != a.end())
cout<<*p++<<endl;
p = a.begin();
a.erase(p,p+5);
p = a.begin();
cout<<"
————————-"
<<endl;
cout<<"
erase 5 element fornt:"
<<endl;
while(p != a.end())
cout<<*p++<<endl;
a.clear();
p = a.begin();
cout<<"
————————-"
<<endl;
cout<<"
clear all element:"
<<endl;
while(p != a.end())
cout<<*p++<<endl;
cout<<a.size()<<endl;
return 0;
}
————————————-
大家可能发现我们上面的程序有些奇怪,我们一直在重新初始化p,有些地方可能理解得过来,有些地方可能大家理解不过来,没关系,下一讲我们再说。
=============================
回复D直接查看目录
原文始发于微信公众号(
C/C++的编程教室
):第六十六讲 细说vector(2)
|