类模板定义
类是对一组对象的公共性质的抽象,而类模板则是对不同类的公共性质的抽象,英雌类模板属于是更高层次的抽象。
格式:
template<模板参数表>
class <类模板名>
{
<类成员声明>
}
其中,<模板参数表>中包含一个或多个用逗号隔开的类型,参数项可以包含数据类型,也可以包含类属类型,如果是类属类型,则必须加class或者typename做前缀。
类模板中的成员函数的定义:
template<模板参数表>
返回值类型<类模板名><类型列表>::<函数名>(参数表)
{
函数体
}
<类型名表>既是类模板中定义中的类型形式参数表中的参数名。
类模板中的成员函数放在类模板的外部的时候,也可以在前面加inline表明内连函数函数。
类模板的实例化
与函数模板不同的是,函数模板的实例化是由编译系统在处理函数调用时自动完成,而类模板的实例化必须由程序员在程序中显示的指定。
代码演示:
#include<iostream> #include<process.h> using namespace std; const int size = 10; template <typename AType> class atype { public: atype() { int i; for (i = 0; i < size; i++) { array[i] = i; } } AType& operator[](int n); private: AType array[size]; }; template <typename AType> AType& atype<AType>::operator[](int n) { if (n<0 || n>size) { cout << "下标" << n << "超出范围!" << endl; exit(1); } return array[n]; } int main() { const int size = 10; atype<int>intob; atype<double>doubleob; int i; cout << "Integer 数组:"; for (i = 0; i < size; i++) intob[i] = i; for (i = 0; i < size; i++) { cout << intob[i] << " "; } cout << endl; cout << "Double 数组:"; for (i = 0; i < size; i++) { doubleob[i] = (double)i / 2; } for (i = 0; i < size; i++) { cout << doubleob[i] << " "; } cout << endl; intob[12] = 100; //下标越界; return 0; }
类模板的实例化创建的结果是一种类型,而类的实例化创建的结果则是一个对象。
使用函数类型参数的类模板
在类模板的《模板参数表中》,必须至少有一个类参数,当然也可以有多个类参数。还可以有非类参数的参数,这样的参数一般称之为函数类型参数,也可以称之为无类型模板参数。
函数参数类型只限于整型,指针和引用,其他的类型(例如浮点型float)则不能使用。传递给函数参数类型的实参要么是整型常量,要么由指向全局函数或对象的指针或引用组成。由于函数参数类型的值不能改变,所以函数参数类型参数本身被看作常量,因此,函数类型参数可以用来设定数组大小。
#include<iostream> #include<process.h> using namespace std; template <typename AType,int size> //size作为函数参数类型参数 class atype //模板类; { public: atype() { int i; for (i = 0; i < size; i++) array[i] = i; } AType& operator[](int n); //"[]"运算符重载;至少含有一个参数; private: AType array[size]; }; template <typename AType,int size> AType& atype<AType, size>::operator[](int n) { //下标越界检查; if (n<0 || n>size) { cout << "下标" <<n<<"超出范围" << endl; exit(1); } return array[n]; } int main() { //10个元素的integer数组类,intob为该类的一个对象; atype<int, 10>intob; //10个元素的double数组类,doublob为该类的一个对象 atype<double, 10>doublob; int i; cout << "integer[]数组类: "; for (i = 0; i < 10; i++) { intob[i] = i; cout << intob[i] << " "; } cout << endl; cout << "doublob[]数组类: "; for (i = 0; i < 10; i++) { doublob[i] = (double)i / 2; cout << doublob[i] << " "; } cout << endl; intob[12] = 100; return 0; }
使用默认参数的类模板
类模板可以包含于通用类型相关的默认参数。当类模板被实例化时,如果没有指定其他的数据类型,则使用默认数据类型。
#include<iostream> #include<process.h> using namespace std; template<typename AType=int,int size=10> class atype //使用默认参数的模板类; { public: atype() { int i; for (i = 0; i < size; i++) { array[i] = i; } } AType& operator[](int n); private: AType array[size]; }; template<typename AType,int size> AType& atype<AType, size>::operator[](int n) { if (n<0 || n>size) { cout << "下标" << n << "越界" << endl; exit(1); } return array[n]; } int main() { //12个元素的integer数组类,inton为模板类的一个对象; atype<int,12>intob; //double数组类,默认10个元素长度, atype<double>doubleob; //数组类,默认int型,默认10个元素长度; atype<>defaultob; int i; cout << "Integer 数组类:"; for (i = 0; i < 12; i++) { intob[i] = i; cout << intob[i] << " "; } cout << endl; cout << "double 数组类:"; for (i = 0; i < 10; i++) { doubleob[i] = (double)i / 2; cout << doubleob[i] << " "; } cout << endl; cout << "默认参数类型:"; for (i = 0; i < 10; i++) { cout << defaultob[i] << " "; } cout << endl; return 0; }
标准模板库STL
c++中包含一个有许多组件的标准库。标准模板库(Standard Template Library,STL)是标准c++标准库的一部分。
容器
“容器”是数据结构,是包含对象的对象。例如,数组,队列,堆栈,树,图等数据结构中的每一个节点都是一个对象。这些结构按照某种特定的逻辑关系组合在一起,就成为了一个新的对象。
容器的分类
在前面介绍c++基础知识的时候,就已经介绍了STL模板库,但是很多人并不知道他的原理,而要想弄懂这个问题就要知道容器的接口是什么。
我们所使用的STL模板斗都是将一些代码封装为一个容器,从而使用它,正是由于这样才使得我们的工作减轻很多负担。要想正确的使用模板,了解他们所对应的函数原型显得很重要。下面我们以vector模板为例,介绍他的原函数:
顺序容器和关联容器
顺序容器将一组具有相同类型的元素一样额的线性形式组织起来。顺序容器分为vector,list,deque三种类型。他们在某些方面很相似。前面已经介绍过了,就不过多介绍。
关联容器具有根据一组索引来快速提取元素的能力,其中元素可以通过键值(key)来访问。四种关联容器可以分为两种:set和map
(1).set是一种集合,其中可以包含0个或多个不重复的以及不排序的元素,这些元素被称为键值。
例如,set集合s
{-2,34,56}
中包含三个键值,不能通过下标来访问集合;
(2).map是一种映像,其中包含0个或多个不排序的元素对,一个元素是不重复的键值,另一个是与键相关联的值。
例如,map集合m
{(first,4),(second,-99),(third,50)}
中包含3对元素。每对元素都由一个键值和相关联的值构成。
map和multimap容器的元素是按照关键字顺序排列的,因此提供按关键字快速查找元素。成员函数find(),count(),lower_bound(),upper_bound()基于元素键值的查找和计数。
#include<iostream> #include<vector> #include<process.h> using namespace std; int main() { int i; vector<int>nums; //整型向量,长度为0; nums.insert(nums.begin(), -99); //在第一处插入数据-99 nums.insert(nums.begin(), 50); nums.insert(nums.end(), 4); for (i = 0; i < nums.size(); i++) { if (nums[i]) cout << nums[i] << " "; } cout << endl; nums.erase(nums.begin()); //删除向量中第一处的值 nums.erase(nums.begin()); for (i = 0; i < nums.size(); i++) { if(nums[i]) cout << nums[i] << " "; } cout << endl; return 0; }
迭代器
对于迭代器的理解,可以理解为面向对象版本的指针,但是和指针又有一些不同。通俗点来说就是指针是迭代器,但迭代器又不仅仅是指针,指针可以指向内存中的一个地址,然后通过这个地址访问相应的内存单元。而迭代器更抽象,它可以指向容器中的一个位置,然后就可以直接访问这个位置的元素。
迭代器的分类
STL迭代器主要包括5中基本类别:输入(input)迭代器,输出(output)迭代器,前向(forward)迭代器,双向(bidirectional)迭代器和随机访问(random access)迭代器。
迭代器在头文件iterator中声明。因为不同类型的容器支持不同的迭代器,所以不必显式指定包含iterator文件也可以使用迭代器。
vector和deque容器支持随机访问。list,map,set,multiset和multimap支持双向访问;
迭代器的使用
可以定义各种容器的迭代器对象(iterator类型对象)。迭代器对象通常被称为迭代子或迭代算子。
eg:
#include<iostream> #include<list> #include<iterator> //迭代器头文件可以省略; using namespace std; int main() { list<int>nums; //整型双向链表 for (int i = 0; i < 10; i++) { nums.insert(nums.end(), i * 10); } list<int>::const_iterator p1; //p1是双向链表的迭代子; cout << "整型双向链表的正向输出:" << endl; for (p1 = nums.begin(); p1 != nums.end(); p1++) //这里p1不再是用循环中常用的小于来判断循环结束条件; { cout << *p1 << " "; } cout << endl; list<int>::reverse_iterator p2; p2 = nums.rbegin(); //反向迭代指向最后一个元素; cout << "逆向输出双向链表中所有元素:" << endl; while (p2 != nums.rend()) { cout << *p2 << " "; p2++; } cout << endl; return 0; }
上面的程序中在使用迭代器的时候,我们发现迭代器的定义有点不一样,那么他们的差距在哪里?
预定义迭代器
预定义迭代器 ++操作方向
iterator 向前
const_iterator 向前
reverse_iterator 向后
const_ireverse_terator 向后
算法
c++中的STL中包含大约70种标准算法。这些算法是用于对容器的数据施加特定操作的函数模板。
STL中几乎所有的算法的头文件都是#include
从对容器的访问性质来说,算法分为只读(及不允许改写元素)和改写(即可以修改元素)两种。从功能上来说可以分为查找,比较,计算,排序,置值,合并,集合,管理等。
通用算法的调用形式
如同STL容器是常用数据结构的类模板一样,STL算法是用于对容器的数据施加特定操作的函数模板。
例如STL中的排序算法:
第一种形式:
template<typename RandomAcessIterator> void sort(RandomAcessIterator first,RandomAcessIterator last);
按照升序排列;
第二种形式:
template<RandomAcessIterator,class Compare> void sort(RandomAcessIterayor first,RandomAcessIterator last,Compare pr)
按照pr函数的排序方式来排序。
通用算法应用
//reverse和sort的应用 #include<iostream> #include<algorithm> #include<vector> using namespace std; bool inoder(int, int); int main() { vector<int>nums; //整型向量,长度为; for (int i = 0; i < 10; i++) { nums.insert(nums.end(), i * 10); } cout << "向量的初始顺序:"; vector<int>::const_iterator p1; for (p1 = nums.begin(); p1 != nums.end(); p1++) { cout << *p1 << " "; } cout << endl; //函数逆置; reverse(nums.begin(), nums.end()); cout << "逆置排列:"; for (int i = 0; i < 10; i++) { cout << nums[i] << " "; } cout << endl; //调用第一种排序方式排序; sort(nums.begin(), nums.end()); cout << "使用第一种方式排序后的结果为:"; for (int i = 0; i < 10; i++) { cout << nums[i] << " "; } cout << endl; //调用第二种方式排序 sort(nums.begin(), nums.end(), inoder); vector<int>::const_iterator p2; cout << "第二种排序方式的输出结果:"; for (p2 = nums.begin(); p2 != nums.end(); p2++) { cout << *p2 << " "; } cout << endl; } bool inoder(int a, int b) { return a > b; }