STL学习(4)- STL算法、迭代器以及泛型与容器的结合使用

有点长,各位可以根据目录选择性观看。

写在前面

STL这个专题是博主个人的总结,私下里整理的时候感觉有些东西像迭代器、find()等一些东西并不是某个容器独有的,没有必要没介绍一个容器都详细写一下这些东西。像泛型等知识,每个容器都要用到,也没必要每个容器都解释一下什么是泛型,所以博主总结了一下这些东西,整体包括一些算法和C++的相关知识,以及在STL中的应用。像insert、erase等方法,虽然大部分容器都有,但是还是有一些差别和不同的使用方式,这里就不多谈了。

泛型简单介绍与使用

泛型,理解以下字面是广泛类型编程,我们平常在写代码的时候对于一个变量,一定有一个确定的数据类型,int、double或者是自己写的类、结构体,但是在工程上我们对于数据的处理却不能仅仅指定一个单独的类型。就像函数的参数,如果对每一种数据类型都写一个单独的函数,就非常浪费时间。但是如果我们使用泛型,在参数上接受“广泛的数据类型”,那么只用写一个函数就能处理多种不同数据类型参数的情况。

在STL中,每个容器仅仅提供了底层的数据结构以及封装好的函数等,例如vector,我们也不知道存放在vector中的数据到底是int还是double,所以,在STL中,利用泛型就能解决多种参数的问题,同时实现代码的复用。

#include<iostream>
#include<string>

using namespace std;

template<typename T>

T add(T a, T b){
	return a + b;
}

int main(){
	int a = 2, b = 3;
	double c = 2.333, d = 1.2222;
	cout << add(a, b) << endl;
	cout << add(c, d) << endl;

	system("pause");
	return 0;
}

上面代码就是一个泛型的例子。template 是关键字,T表示一个待实例化的类型。我们可以理解为“代号T”,任何一个数据都能看成这个“代号T”。add函数的2个形参都是T数据类型,返回值也是T数据类型。我们第一次调用add函数,2个参数是int类型,那么T就代表了int,所以返回值也是int;第二次调用,2个参数是double,那么返回值也是double。

对于STL的容器而言,在接收参数的时候也有这样的需求,接受的参数是T,那么具体的函数实现都是以“代号T”来进行,无论是int还是double,因为都是T,所以都能接收。

#include<iostream>
#include<vector>

using namespace std;

int main(){
	vector<int> v;
	vector<double> v2;
	v.push_back(12);
	v2.push_back(2.33);
	cout << v[0] << endl;
	cout << v2[0] << endl;

	system("pause");
	return 0;
}

以上代码就是STL中使用最多的泛型了,尖括号“<>”中就是指出泛型T的具体数据类型,那么我们在使用容器的时候就只能放这个一个数据类型。等于说我们有vector<T>,T当然可以用任何一个数据类型来代替,但是代替后我们就必须用这个数据类型。v与v2,一个是int一个是double,但是全部都能存进去。实际上,STL中vector的代码只有只用,因为用了泛型,所以可以接收多种不同的数据类型。

同样的,我们在使用容器的时候都要利用泛型,这样才能确定具体要存放什么数据。例如:

#include<iostream>
#include<vector>
#include<map>
#include<set>

using namespace std;

int main(){
	vector<int> v;
	map<int, string> m;
	set<int> s;

	system("pause");
	return 0;
}

使用容器一定要确定泛型。

iterator

迭代器,因为有迭代,我们首先想到的就是for循环,迭代器也一样,就是遍历容器里的数据对象。对储存在容器中的数据进行处理的时候,迭代器能够按照预先的顺序(一般都是正向迭代,但是我们也可以逆向迭代,这个下面会说)从一个成员移动到下一个成员。换而言之,迭代器就是一个指针,不断地在容器中按顺序移动,指向不同的成员。

实际上迭代器是个泛化指针,也是利用了泛型。当生成一个容器的迭代器的时候,迭代器就指向了一个成员,然后可以通过自加一或者自减一来进行移动,就可以对数据进行操作。

上面不止一次谈到了“容器”,每个容器都能生成属于自己的迭代器,这样迭代器就能按照一定的顺序进行遍历,因为不同的容器底层的实现不同,迭代器的遍历方式也不同。由于容器利用了泛型,所以数据类型也不同,那么迭代器的数据类型也不同。所以说每种容器都有自己的迭代器。

vector<int> v;
vector<int>::iterator it;

这就是标准声明,声明一个iterator类型,属于一个vector容器,且数据类型为int的迭代器。这里注意,此时只是声明了it,并不代表it就指向了具体的位置。就好比int *a,只是声明了指针,并不代表已经指向了一个位置。例如:

vector<int> v1;
vector<int> v2;
vector<int>::iterator it;

仅仅是声明。但是注意,毕竟it是指针,指向的一定是vector<int>的数据,所以赋值的时候也不能有错误。下面给一段代码:

#include<iostream>
#include<vector>

using namespace std;

int main(){
	int arr[5] = { 1,3,5,8,9 };
	vector<int> v(arr,arr+5);
	vector<int>::iterator it;
	it = v.begin();
	for (; it != v.end(); it++) {
		cout << *it << endl;
	}

	system("pause");
	return 0;
}

这就是迭代器的简单使用。begin()函数,返回的是容器的首指针,也就是说,此时it指向的是容器中“1”的内存。for循环中判断退出的控制条件是 it != v.end(),end()函数,返回的是容器的尾指针的后一位,等于说已经“越界”了。*it,不再赘述(对指针不理解的话要先学一学了- -)。

我们上面说过,迭代器是按照顺序移动的,但是在for循环中我们明明是自己令it++来移动的。实际上按照顺序移动或者说正向迭代,指的是一个方向。有正向迭代一定有逆向迭代,逆向迭代的时候仍然使用it++,我们会发现指针在反向移动。所以说,it++指的是一个方向,指针正向移动的时候是正向还是反向,决定了迭代器的迭代顺序。

虽然话是这样说,但是正向迭

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值