因为时间紧,所以飞速讲完类模板

要想迅速掌握函数模板,需要函数模板的知识储备(传送门
我就勉为其难,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> OR template <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);
  • 编译这个新建的函数
重载函数模板
  • 允许有其它同名的函数模板,但参数必须不同
  • 允许有其它同名的函数,但参数必须不同
  • 编译器挑选最佳匹配的函数或模板
    • 首先,编译器寻找和使用最符合函数名和参数类型的函数调用。
    • 如果找不到,则检查是否可以用函数模板产生符合函数名和参数类型的模板函数。

类模板

  • 类是对问题空间的抽象,而类模板则是对类的抽象,即更高层次上的抽象
  • 程序中可以首先定义一个类模板,然后通过使用不同的实参生成不同的类
  • 类模板的定义格式:
 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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值