《C++大学教程》学习笔记(十六)
本节并没有参考Deitel的书,而是参考了本科教材以及郑莉的书。
1.模版
1.1基本知识
若一个程序的功能是对任意类型的数据作同样的处理,则将所处理的数据类型说明为参数,就可以把这个程序改写为模版。模版提供了重用程序源代码的有效方法,方便了大规模的软件开发。
C++中的模版分为类模版(class template)和函数模版(function template)。
1.2函数模版
要注意区分函数模版与模版函数:
- 函数模版就是数据类型参数(抽象)化的函数定义,是一个函数族,代表的是一类函数。
- 当编译系统发现用指定数据类型调用函数模版时,就创建了一个模版函数,模版函数是一个实例化的具体函数。
关于函数模版的定义和使用,以及重载模版函数,下面给出一个实例:
#include <iostream>
#include <string>
using namespace std;
//定义一个函数模版
template <class T>
T Max(T a,T b){
cout << "Template function called...\n";
return (a > b ? a : b);
}
//定义第一个重载模版函数
string Max(string a,string b){
cout << "string function called...\n";
return (a.compare(b) > 0 ? a : b) ;
}
//定义第二个重载模版函数,一定要有const
const char * Max(const char* a,const char* b){
cout << "const char* function called...\n";
return (strcmp(a,b) > 0 ? a : b) ;
}
int main(){
string str1 = "abcd";
string str2 = {"abab"};
cout << Max(123, 124) << endl; //调用函数模版生成对应的模版函数
cout << Max(3.134,3.135) << endl; //调用函数模版生成对应的模版函数
cout << Max(str1,str2) << endl; //首选函数名、参数类型都匹配的函数
cout << Max("abcd", "abab") << endl; //C++认为这里的"abcd"的类型是const char*
return 0;
}
运行结果如下图所示:
从上面这个实例可以看出,当编译器在处理重载模版函数的问题时,遵循的原则是:首选函数名、参数类型都匹配的具体函数,再找模版。
另外我发现当输入的参数是“abcd”这种情况时,编译器会无视定义成string或者char*的重载函数,而是选择函数模版生成具体的模版函数。这是由于C++默认“abcd”这类输入的类型是const char*,因此我们得把函数的参数及返回类型都改成const char*。
1.3类模版
同样,要注意区分类模版和模版类:
- 类模版是模版的定义,不是一个实实在在的类,定义用到通用类型参数。
- 模版类是实实在在的类定义,是类模版的具体实例化。
同样给出一个类模版的实例:
#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class MyTemplateClass {
private:
T1 x;
T2 y;
public:
MyTemplateClass(T1 xx,T2 yy)
:x(xx),y(yy)
{}
void print(){
cout << "x:" << x <<endl
<< "y:" << boolalpha << y << endl;
//boolalpha代表以布尔值输出而不是0和1
}
};
int main(){
MyTemplateClass<double, string> mytc1(3.222,"hello");
MyTemplateClass<int, bool> mytc2(64,false);
mytc1.print();
mytc2.print();
return 0;
}
运行结果是:
这里的类模版用到了两种类型的参数,所以实例化时也要给出两个具体的类型。
2.STL(Standard Template Library)
2.1简介
C++提供的标准模版库(STL)是面向对象程序设计(OOP)与泛型程序设计(Generic Programming)思想相结合的一个良好典范。
STL为通用容器、迭代器、和建立在它们之上的算法提供模版。使程序设计者无需了解STL的基本原理,便可以使用其中的数据结构和算法。
构建STL框架最关键的四个组件是容器(container)、迭代器(iterator)、算法(algorithm)和函数对象(function object)。
其中,算法处于核心地位,迭代器如同算法和容器之间的桥梁。
2.2容器
容器类是容纳、包含一组元素或元素集合的对象。
当容器类包含不同类型的元素时,称为异类容器类;当包含相同类型的元素时,称为同类容器类。
容器类库包含7种基本的容器:
- 向量(vector)
- 双端队列(deque)
- 列表(list)
- 集合(set)
- 多重集合(multiset)
- 映射(map)
- 多重映射(multimap)
这7种容器中,向量、双端队列和列表属于顺序容器(sequence container),而(多重)集合以及(多重)映射属于关联容器(associative container)。
2.3迭代器
迭代器是面向对象版的指针,它们提供了访问容器和序列中每个元素的方法。
迭代器有5种基本类别:
- 输入
- 输出
- 前向
- 双向
- 随机
以及两种迭代器适配器:逆向迭代器适配器和插入迭代器适配器。
2.4适配器
适配器是一种接口类,为已有的类提供新的接口。
适配器可分为:
- 容器适配器
- 迭代器适配器
- 函数对象适配器
2.5实例
下面给出一个综合了容器、迭代器、算法及函数对象的实例,代码如下所示:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main(){
vector<int> v(5);
ostream_iterator<int> output1(cout," "); //绑定标准输出装置,每个输出元素的分隔符为空格
ostream_iterator<int> output2(cout,","); //绑定标准输出装置,每个输出元素的分隔符为逗号
int n;
cout << "Input 5 integers:\n";
vector<int>::iterator pos = v.begin(); //使用迭代器
while (pos != v.end()) {
cin >> n;
*pos = n; //与指针用法很相似
pos++;
}
cout << "Vector v contains: ";
copy(v.begin(), v.end(), output1);
sort(v.begin(), v.end()); //从小到大排序,默认
cout << "\nAfter sorted in ascending,Vector v contains: ";
copy(v.begin(), v.end(), output1); //copy()会将存储在v中的每个元素一一写到由output所表示的ostream上头,每个元素皆以空格符分隔开来。
sort(v.begin(), v.end(), greater<int>()); //从大到小排序,需要使用函数对象,greater的意思是大于
cout << "\nAfter sorted in descending,Vector v contains: ";
copy(v.begin(), v.end(), output2); //copy()会将存储在v中的每个元素一一写到由output所表示的ostream上头,每个元素皆以,分隔开来。
cout << endl;
return 0;
}
执行的结果如下图所示:
重要的信息都在注释里。