第六十六讲 细说vector(2)


上一讲我们说了一些类型的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;


}
————————————-

第六十六讲 细说vector(2)大家可能发现我们上面的程序有些奇怪,我们一直在重新初始化p,有些地方可能理解得过来,有些地方可能大家理解不过来,没关系,下一讲我们再说。
=============================
回复D直接查看目录

原文始发于微信公众号(

C/C++的编程教室

):第六十六讲 细说vector(2)

|

发表评论