要想迅速掌握函数模板,需要函数模板的知识储备(传送门)
我就勉为其难,xue微花一点时间再深入讲解一下:
简介
模板 ( template ) :
利用一种完全通用的方法来设计函数或 x类而不必预先说明将被使用的每个对象的类型,利用模板功能可以构造相关的函数或类的系列,因此模板也可称为参数化的类型。
在C++语言中,模板可分为类模板 ( class template ) 和函数模板 ( function template ) 。
区分:
函数模板和类模板:具有各种形状的模板
实际函数和模板特化: 形状相同,需要涂上不同的颜色
定义模板
我又拿出了这个可爱俏皮的函数模板:
template <typename T>
T maximum(T a,T b,T c) {
T ans,t;
ans=((t=(a>b)?a:b)>c)? t:c;
return ans;
}
看到第一行:
template <typename T>
template <class T>
ORtemplate <typename T>
template关键字+<template parameter list ( 模板参数列表 ) >
模板参数列表:
- 参数称为formal type parameter ( 形式类型参数 )。
- 占位符 ( T ) , 在函数调用时替换为基本数据类型或用户自定义数据类型。
- 每个参数都必须以关键词class ( 或typename ) 起头,参数和参数之间必须以逗号分隔, 如
<class T, class V>
我们可以抽象出函数模板的大体构架:
template <类型形参表>
返回值类型 函数名(形参表)
{
函数定义体
}
注意:
- <类型形参表>可以有一到若干个形参,各形参前必须加上class关键字,表示传递类型,当有多个形参时,各形参间用逗号分隔。
- <类型形参表>中的每个形参表示一种数据类型。“形参表”中至少有一个形参的类型必须用<类型形参表>中的形参来定义 ( 简单来说,必须有一个需要用户自定义的类型 )。
- 函数模板只是说明,不能直接执行,需要特化为模板函数后才能执行。
函数模板处理过程
我们需要简单了解函数模板再计算机内部的处理方式,这样有助于我们之后的学习
- 在程序中说明了一个函数模板
- 例如:
template<typename T>
void printArray(const T *array,int count);
- 例如:
- 编译系统发现有一个相应的函数调用
- 例如:
printArray(intArray,count);
- 例如:
- 编译器寻找和使用最符合函数名和参数类型的函数模板
- 根据实参中的类型来确认是否匹配函数模板中对应的形参, 然后生成一个重载函数。(该重载函数的定义体与函数模板的函数定义体相同,称之为函数模板的特化 ( functiontemplate specialization ) )
- 例如:对于int类型的模板特化:
void printArray(const int* array,int count);
- 例如:对于int类型的模板特化:
- 编译这个新建的函数
重载函数模板
- 允许有其它同名的函数模板,但参数必须不同
- 允许有其它同名的函数,但参数必须不同
- 编译器挑选最佳匹配的函数或模板
- 首先,编译器寻找和使用最符合函数名和参数类型的函数调用。
- 如果找不到,则检查是否可以用函数模板产生符合函数名和参数类型的模板函数。
类模板
- 类是对问题空间的抽象,而类模板则是对类的抽象,即更高层次上的抽象
- 程序中可以首先定义一个类模板,然后通过使用不同的实参生成不同的类
- 类模板的定义格式:
template<class 类型参数> //模板声明
class 类名 // 类定义
{ ... };
- 每个 " 类型形参 " 前必须加class关键字,对类模板进行实例化时,代表某种数据类型;也可以是普通数据类型形参,特化时就代表具体数据
在下面的栗子中我们可以看到,在类型形参中的普通数据类型形参,特化后就有着和常量一样的效力,可以用来直接定义数组
template<class arg1,int arg2,class arg3>
class myclass {
arg1 buffer[arg2];
arg3 x; //类的定义体
};
- 类模板中成员函数可以放在类模板的定义体中 ( 此时与类中的成员函数的定义方法一致 ) 定义,也可以放在类模板的外部来定义,格式为:
template <类型形参表>
函数返回类型 类模板名<类型名表>::成员函数(形参)
注意:
- 类模板定义只是对类的描述,ta本身还不是一个实实在在的类
- 类模板不能直接使用,必须先特化为相应的模板特化,创建特化类的对象(实例) 后,才可使用
- 可以用以下方式创建类模板的实例
类模板名<类型实参表> 对象名表
此处的<类型实参表>要与该模板中的<类型形参表>匹配,要具有同样的顺序和类型,否则会产生错误。
一个很重要的类模板实例
栈:先进后出
数据结构:
- size:栈的大小
- top: 栈顶位置,初始值为-1(空栈)
- stackPtr :存储栈中元素的一组连续存储空间
操作:- push:如果栈不满,则向栈中增加一个元素,top增1
- pop: 如果栈不空,则从栈中弹出一个元素,top减1
- isEmpty:如果top为-1,则栈为空
- isFull:如果top为size-1,则栈满
需求:建立栈的类模板,满足不同类型的元素
//Stack.h
//创建类模板Stack,包含类型参数T
#ifndef STACK_H
#define STACK_H
template<typename T> //template+模板参数列表
class Stack
{
public:
Stack(int =10); // default constructor (Stack size 10)
// destructor
~Stack() {delete [] stackPtr;}
//成员函数在参数列表中使用类型参数T
bool push(const T&); // push an element onto the Stack
bool pop(T&); // pop an element off the Stack
// determine whether Stack is empty
bool isEmpty() const {return top==-1;}
// determine whether Stack is full
bool isFull() const {return top==size-1;}
private:
int size;
int top; // location of the top element (-1 means empty)
T *stackPtr; // pointer to internal representation of the Stack
};
//在类模板的外部定义成员函数
//template<类型形参表>
//函数返回类型 类模板名<类型名表>::成员函数(形参)
template<typename T>
Stack<T>::Stack(int s):size(s>0 ? s:10),top(-1),stackPtr(new T[size]) {}
template<typename T>
bool Stack<T>::push(const T &pushValue) {
if (!isFull()) {
stackPtr[++top]=pushValue; // place item on Stack
return true; // push successful
}
return false; // push unsuccessful
}
template<typename T>
bool Stack<T>::pop(T &popValue) {
if (!isEmpty()) {
popValue=stackPtr[top--];
return true;
}
return false;
}
#endif
// Stack class template test program.
#include <iostream>
#include "Stack.h" // Stack class template definition
using namespace std;
int main()
{
Stack<double> doubleStack(5);
double doubleValue=1.1;
cout<<"Pushing elements onto doubleStack\n";
while (doubleStack.push(doubleValue)) {
cout<<doubleValue<<' ';
doubleValue+=1.1;
}
cout<<"\nStack is full. Cannot push "<<doubleValue
<<"\n\nPopping elements from doubleStack\n";
while (doubleStack.pop(doubleValue))
cout<<doubleValue<<' ';
cout<<"\nStack is empty. Cannot pop\n";
Stack<int> intStack;
int intValue=1;
cout<<"\nPushing elements onto intStack\n";
while (intStack.push(intValue)) {
cout<<intValue<<' ';
intValue++;
}
cout<<"\nStack is full. Cannot push "<<intValue<<"\n\nPopping elements from intStack\n";
while (intStack.pop(intValue))
cout<<intValue<<' ';
cout<<"\nStack is empty. Cannot pop"<<endl;
return 0;
}
为了避免main函数中重复的逻辑,我们可以使用函数模板
#include <iostream>
#include <string>
#include "Stack.h"
using namespace std;
template<typename T>
void testStack(Stack<T> &theStack,T value,T increment,const string stackName) {
cout<<"\nPushing elements onto "<<stackName<<'\n';
while (theStack.push(value)) {
cout<<value<<' ';
value+=increment;
}
cout<<"\nStack is full. Cannot push "<<value<<"\n\nPopping elements from "<<stackName<<'\n';
while (theStack.pop(value))
cout<<value<<' ';
cout<<"\nStack is empty. Cannot pop"<<endl;
}
int main()
{
Stack<double> doubleStack(5);
Stack<int> intStack;
testStack(doubleStack,1.1,1.1,"doubleStack");
testStack(intStack,1,1,"intStack");
return 0;
}
非类型参数和类模板的缺省类型
康大大的blog不够香嘛~
这里老师讲的乱七八糟,实际上想说的只有一句话:
在类型形参中的普通数据类型形参,特化后就有着和常量一样的效力,可以用来直接定义数组等
举个最简单的栗子:
template <class T,int size>
class Stack {
T buffer[size];
int top;
public:
Stack() {top=-1;}
bool push(const T &x);
bool pop(T &x);
bool isEmpty() const {return top==-1;}
bool isFull() const {return top==size-1;}
};
template <class T,int size>
bool Stack<T,size>::push(const T &x) {
if (!isFull()) {
buffer[++top]=x;
return true;
}
return false;
}
template <class T,int size> bool Stack<T,size>::pop(T &x) {
if (!isEmpty()) {
x=buffer[top--];
return true;
}
return false;
}
// 特化类模板
Stack<int,100> st1;
Stack<double,200> st2;
无类型参数和类模板的缺省类型
explicit specializations(显式特化)
- 当某一特殊数据类型不能使用通用类模板时,可以定制处理
即:重定义该类型的类模板。 - 显式特化: 直接给出类型实参
class Stack<Employee> { … };
下面我要飞速介绍(不要求掌握的得心应手的知识点,我们就惊鸿一撇):
类模板及其友元
关于友元的介绍
一句话总结:被friend声明的内容可以在该类class大括号之外的区域,访问该类的非公有成员
template<typename T>
class X { ... };
- 函数声明为类模板的每一个特化的友元
friend void f1();
- f1 is a friend of X< double >, X< string >, etc.
- 函数声明为类模板的某一类型参数的特化的友元
friend void f2(X<T> &);
- f2(X< float > &) is a friend of X< float >, but not a friend of X< string >
- 另一个类的成员函数可以声明为类模板每一个特化的友元 ( 无参数 )
friend void A::f3();
- f3 of class A is a friend of X< double >, X< string >, etc.
- 另一个类的成员函数可以声明为类模板某个类型参数特化的友元 ( 带参数 )
friend void C<T>::f4(X<T> &);
- C< float >::f4(X< float > &) is a friend of X< float >, but not a friend of X< string >
- 另一个类可以声明为类模板的每一个实例的友元
friend class Y;
( 无类型参数 )- Every member function of class Y is a friend of X< double >, X< string >, etc.
- 另一个类可以声明为类模板的某个类型参数实例的友元
friend class Z<T>;
( 有类型参数 )- Class-template specialization Z< float > is a friend of X< float >, Z< string > is a friend of X< string >, etc.
类模板中重载<<和>>,也是非常重要的内容,我就不废话了直接上大神链~
(注意对于<<和>>的重载,要直接写在类定义内部)
链表
其实,我觉得这才是这一章中最重要的知识点,但是疲惫的我决定直接上栗子
用Linklist ( 链表 ) 实现一个栈模板:
template<class T>
class linkedStack;
template<class T>
class Node {
friend linkedStack<T>; //友元类
private:
T data;
Node<T> *link;
};
template<class T>
class linkedStack //栈模板定义
{
friend ostream& operator<<(ostream &out,linkedStack<T> &s) {
T elem;
while (!s.IsEmpty()) {
s.Delete(elem);
out<<elem<<endl;
}
return out;
}
public:
linkedStack() {top=0;}
~linkedStack();
bool IsEmpty() const {return top==NULL;}
bool IsFull() const;
linkedStack<T> &Add(const T&x);
linkedStack<T> &Delete(T &x);
private:
Node<T> *top;
};
template<class T>
linkedStack<T>::~linkedStack() {
Node<T> *next;
while (top!=0) {
next=top->link;
delete top;
top=next;
}
}
template<class T>
bool linkedStack<T>::IsFull() const {
try {
Node<T> *p=new Node<T>;
delete p;
return false;
}
catch (...) {return true;}
}
template<class T>
linkedStack<T> & linkedStack<T>::Add(const T &x) {
Node<T> *p=new Node<T>;
p->data=x;
p->link=top;
top=p;
return *this;
}
template<class T>
linkedStack<T> & linkedStack<T>::Delete(T &x) {
Node<T> *temp=NULL;
x=top->data;
temp=top->link;
delete top;
top=temp;
return *this;
}
int main()
{
linkedStack<int> l;
for (int i=0;i<=10;i++) {
l.Add(i*10);
}
cout<<l;
return 0;
}
最后我们xue微来了解一下:
类模板及其静态数据成员
从类模板实例化的每个模板特化有自己的static数 据成员
- 该模板类(特化)的所有对象共享一个static数据成员
- static数据成员必须在类外部被定义,在文件范围内被初始化
template <class T>
class A {
static int x;
};
Template <class T> int A<T>::x=0;
A<int> a1,a2; // a1和a2共享一个x
A<double> b1,b2; // b1和b2共享一个x