C++进阶--模板

文章详细介绍了C++中的函数模板和类模板的使用,包括普通模板、模板的具体化与实例化。函数模板允许创建通用函数,如`Swap`函数,能处理不同类型的参数。类模板则用于创建通用类,如`Stack`,可以用来存储不同类型的数据。此外,还讨论了模板的具体化,即为特定类型创建专门的模板实现,以及模板实例化,用于提前生成特定类型的模板代码。
摘要由CSDN通过智能技术生成

目录

一.函数模板

1.普通模板

2.模板的具体化与实例化

二.类模板

1.定义类模板

2.使用类模板

3.深入探讨模板类(略)

4.数组模板示例和非类型参数

5.模板多功能性

6.模板具体化

7.成员模板

8.将模板用作参数(略)

9.模板类和友元(略)

10.模板别名(略)


一.函数模板

1.普通模板

格式:

// 声明
template <typename T>  // or class T
void Swap(T &a, T &b);

// 定义
template <typename T>  // or class T
void Swap(T &a, T &b)
{
    T temp;   // temp a variable of type T
    temp = a;
    a = b;
    b = temp; 
}

第一行指出要建立一个模板,并将类型命名为T,关键字 template typename 是必须的;

typename  也可以换成关键字 class 来创建模板:

// 声明
template <class T>  // or class T
void Swap(T &a, T &b);

// 定义
template <class T>  // or class T
void Swap(T &a, T &b)
{
    T temp;   // temp a variable of type T
    temp = a;
    a = b;
    b = temp; 
}

调用:

int i = 10;
int j = 20;
Swap(i,j);

double x = 24.5;
double y = 81.7;
Swap(x,y); 

第一个 Swap 函数接收了两个 int 参数,因此编译器生成 int 版本,也就是用 int 替换所有的 T ,生成下面的定义:

void Swap(int &a, int &b)
{
    int temp;   // temp a variable of type int 
    temp = a;
    a = b;
    b = temp; 
}

第二个 Swap 函数接收了两个 double 参数,因此编译器生成 double 版本,也就是用 double 替换所有的 T ,生成下面的定义:

void Swap(double &a, double &b)
{
    double temp;   // temp a variable of type double 
    temp = a;
    a = b;
    b = temp; 
}

当然,模板也可以用在函数重载中:

template <typename T>     // original template
void Swap(T &a, T &b);

template <typename T>     // new template
void Swap(T *a, T *b, int n);

调用时会根据输入参数情况,调用相应函数。

2.模板的具体化与实例化

具体化:

对于给定的函数名,有非模板、普通模板、显式具体化模板、重载版本;

优先级为:非模板 > 显式具体化模板 > 普通模板

而显示具体化作用是生成指定类型的模板:

struct job
{
    ...
}

//普通模板
template <typename T>  // or class T
void Swap(T &a, T &b);

//具体化模板
template <> void Swap<job>(job &j1, job &j2);

这时就生成一个类型为 job 的模板,当参数为 job 时,将优先调用:

double u,v;
Swap(u,v);

job a,b;
Swap(a,b);

上述程序第一次将调用普通模板,第二次会调用 job 类型模板;

具体化的意义在于,可以以某个类型定义单独的函数,如下所示:

template <typename T>
void Swap(T &a, T &b)    // general version
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}

// swaps just the salary and floor fields of a job structure

template <> void Swap<job>(job &j1, job &j2)  // specialization
{
    double t1;
    int t2;
    t1 = j1.salary;
    j1.salary = j2.salary;
    j2.salary = t1;
    t2 = j1.floor;
    j1.floor = j2.floor;
    j2.floor = t2;
}

此时,job 的具体化模板可以单独修改某个成员变量;

对于普通模板,因为事先不知道是什么类型,所以不方便修改,比如int型就没有成员变量这一说;

实例化:

格式:

//普通模板
template <typename T>     
void Swap(T &a, T &b);

//具体化模板
template <> void Swap<job>(job &j1, job &j2);

//实例化模板
template void Swap<char>(char &j1, char &j2);

与具体化区别在于:

1、template后面少了一个 <>

2、调用时使用普通模板生成 char 类型的模板,所以不用单独定义;而具体化是要单独定义的;

3、直接在调用前声明,后面即可使用,不用像具体化那样声明、定义都要有,使用如下:

int main(void)
{
    template void Swap<char>(char &j1, char &j2);
    char a,b;
    Swpa(g,h);
}

二.类模板

1.定义类模板

示例:

// stacktp.h -- a stack template
#ifndef STACKTP_H_
#define STACKTP_H_
template <class Type>
class Stack
{
private:
    enum {MAX = 10};    // constant specific to class
    Type items[MAX];    // holds stack items
    int top;            // index for top stack item
public:
    Stack();
    bool isempty();
    bool isfull();
    bool push(const Type & item); // add item to stack
    bool pop(Type & item);        // pop top into item
};

template <class Type>
Stack<Type>::Stack()
{
    top = 0;
}

template <class Type>
bool Stack<Type>::isempty()
{
    return top == 0;
}

template <class Type>
bool Stack<Type>::isfull()
{
    return top == MAX;
}

template <class Type>
bool Stack<Type>::push(const Type & item)
{
    if (top < MAX)
    {
        items[top++] = item;
        return true;
    }
    else
        return false;
}

template <class Type>
bool Stack<Type>::pop(Type & item)
{
    if (top > 0)
    {
        item = items[--top];
        return true;
    }
    else
        return false; 
}

#endif

(1)和模板函数一样,模板类以下面代码开头:

template <class Type>

或者:

template <typename Type>

当模板被调用时,Type 将被具体的类型值(int、string)取代;

(2)成员变量类型都用 Type 声明:

Type items[MAX];

(3)成员函数都以相同的模板声明打头

template <class Type>
bool Stack<Type>::isempty()
{
    return top == 0;
}

注意这里将类限定符从 Stack:: 改为 Stack<Type>:: 。

如果是在类中就定义了成员函数的方法(内联定义),则可以省略模板前缀和类限定符;

不能将模板成员函数放在独立的实现文件中,因为模板不是函数,不能单独编译,最简单的方法是将所有的模板信息放在一个头文件中,在使用这些模板时包含该头文件即可。

2.使用类模板

示例:

// stacktem.cpp -- testing the template stack class
#include <iostream>
#include <string>
#include <cctype>
#include "stacktp.h"
using std::cin;
using std::cout;

int main()
{
    Stack<std::string> st;   // create an empty stack
    char ch;
    std::string po;
    cout << "Please enter A to add a purchase order,\n"
         << "P to process a PO, or Q to quit.\n";
    while (cin >> ch && std::toupper(ch) != 'Q')
    {
        while (cin.get() != '\n')
            continue;
        if (!std::isalpha(ch))
        {
            cout << '\a';
            continue;
        }
        switch(ch)
        {
            case 'A':
            case 'a': cout << "Enter a PO number to add: ";
                      cin >> po;
                      if (st.isfull())
                          cout << "stack already full\n";
                      else
                          st.push(po);
                      break;
            case 'P':
            case 'p': if (st.isempty())
                          cout << "stack already empty\n";
                      else {
                          st.pop(po);
                          cout << "PO #" << po << " popped\n";
                          break;
                      }
        }
        cout << "Please enter A to add a purchase order,\n"
             << "P to process a PO, or Q to quit.\n";
    }
    cout << "Bye\n";
    // cin.get();
    // cin.get();
    return 0; 
}

(1)实例化模板:

Stack<std::string> st; 

此时编译器将生成一个类声明和类方法,且用 std::sting 代替 Type;

(2)使用模板方法

实例化模板后,对象将像使用普通类方法那样使用模板类方法:

st.isfull()

3.深入探讨模板类(略)

4.数组模板示例和非类型参数

示例:

//arraytp.h  -- Array Template
#ifndef ARRAYTP_H_
#define ARRAYTP_H_

#include <iostream>
#include <cstdlib>

template <class T, int n>
class ArrayTP
{
private:
    T ar[n];
public:
    ArrayTP() {};
    explicit ArrayTP(const T & v);
    virtual T & operator[](int i);
    virtual T operator[](int i) const;
};

template <class T, int n>
ArrayTP<T,n>::ArrayTP(const T & v)
{
    for (int i = 0; i < n; i++)
        ar[i] = v;
}

template <class T, int n>
T & ArrayTP<T,n>::operator[](int i)
{
    if (i < 0 || i >= n)
    {
        std::cerr << "Error in array limits: " << i
            << " is out of range\n";
        std::exit(EXIT_FAILURE);
    }
    return ar[i];
}

template <class T, int n>
T ArrayTP<T,n>::operator[](int i) const
{
    if (i < 0 || i >= n)
    {
        std::cerr << "Error in array limits: " << i
            << " is out of range\n";
        std::exit(EXIT_FAILURE);
    }
    return ar[i]; 
}

#endif

(1)非类型参数

template <class T, int n>

关键字 class/typename 指出 T 为类型参数;

int 指出 n 为类型为 int ,这种参数成为非类型 / 表达式参数;

 (2)数组模板

用 n 指定数组大小,T 指定数组类型;

T ar[n];

之后在成员函数中使用 ar[i] ,就相当于使用普通的一维数组;

5.模板多功能性

(1)递归使用模板

ArrayTP< ArrayTP<int,5>, 10> twodee;

这使得 twodee 是一个包含10个元素的数组,其中每个元素都是一个包含5个int元素的数组,等价常规数组为:int twodee[10][5];

使用如下:

// twod.cpp -- making a 2-d array
#include <iostream>
#include "arraytp.h"
int main(void)
{
    using std::cout;
    using std::endl;
    ArrayTP<int, 10> sums;
    ArrayTP<double, 10> aves;
    ArrayTP< ArrayTP<int,5>, 10> twodee;
    
    
    int i, j;
    
    for (i = 0; i < 10; i++)
    {
        sums[i] = 0;
        for (j = 0; j < 5; j++)
        {
            twodee[i][j] = (i + 1) * (j + 1);
            sums[i] += twodee[i][j];
        }
        aves[i] = (double) sums[i] / 10;
    }
    for (i = 0; i < 10; i++)
    {
        for (j = 0; j < 5; j++)
        {
            cout.width(2);
            cout << twodee[i][j] << ' ';
        }
        cout << ": sum = ";
        cout.width(3);
        cout  << sums[i] << ", average = " << aves[i] << endl;
    }
    
    cout << "Done.\n";
    // std::cin.get();
    return 0;
}

(2)使用多个类型参数

// pairs.cpp -- defining and using a Pair template
#include <iostream>
#include <string>
template <class T1, class T2>
class Pair
{
private:
    T1 a;
    T2 b;
public:
    T1 & first();
    T2 & second();
    T1 first() const { return a; }
    T2 second() const { return b; }
    Pair(const T1 & aval, const T2 & bval) : a(aval), b(bval) { }
    Pair() {}
};

template<class T1, class T2>
T1 & Pair<T1,T2>::first()
{
    return a;
}
template<class T1, class T2>
T2 & Pair<T1,T2>::second()
{
    return b;
}

int main()
{
    using std::cout;
    using std::endl;
    using std::string;
    Pair<string, int> ratings[4] =
    {
        Pair<string, int>("The Purpled Duck", 5),
        Pair<string, int>("Jaquie's Frisco Al Fresco", 4),
        Pair<string, int>("Cafe Souffle", 5),
        Pair<string, int>("Bertie's Eats", 3)
    };

    int joints = sizeof(ratings) / sizeof (Pair<string, int>);
    cout << "Rating:\t Eatery\n";
    for (int i = 0; i < joints; i++)
        cout << ratings[i].second() << ":\t "
             << ratings[i].first() << endl;
    cout << "Oops! Revised rating:\n";
    ratings[3].first() = "Bertie's Fab Eats";
    ratings[3].second() = 6;
    cout << ratings[3].second() << ":\t "
         << ratings[3].first() << endl;
   // std::cin.get();
   return 0; 
}

模板包含多个类型参数:

template <class T1, class T2>

在使用时提供多个类型:

Pair<string, int> ratings;

(3)默认类型模板参数

template <class T1, class T2 = int>
class Topo
{
...
}

这样,如果省略了T2的值,编译器将使用 int; 

6.模板具体化

(1)隐式实例化

前面所用的都是隐式实例化:

ArrayTP<int, 100> stuff;

编译器在需要对象之前,不会生成类的隐式实例化,但调用这一句,会生成相应的类定义,并根据类定义生成一个对象;

(2)显式实例化

使用关键字,且声明在类模板定义所在命名空间中,并指出所需类型:

template class ArrayTP<int, 100>;

此时虽然没有创建对象,但编译器会生成相应的类声明及其方法;

(3)显式具体化

针对不同类型定义的具体化模板,该模板为具体类型的模板,而不是上面所说的泛型定义的模板;

当编译器请求匹配实例化时,将优先使用具体化模板,如下为专供 const char *类型的模板:

template <> class SorteArray<const char char *>
{

}

其中的方法可以不同于通用模板的方法,这样特定类型调用时,可以使用这个类型模板提供的特殊方法;

如下所示,当请求 const char * 类型的 SorteArray 模板时,编译器将使用上述专用的定义,而不是通用模板定义:

SorteArray<int> scores;            //使用通用模板
SorteArray<const char *> dates;    //使用特定类型模板

(4)部分具体化

部分具体化可以给模板的通用类型参数之一指定具体类型:

//通用模板
template <class T1, class T2> 
class Pair
{
    ...
}

//部分具体化模板
template <class T1> 
class Pair <T1, int> 
{
    ...
}

关键字 template 后面 <> 是没有被具体化的类型参数;

如果指定所有类型,则 <> 将为空,此时就是显式具体化:

template <> 
class Pair <int, int> 
{
    ...
}

 以上多种模板,在实际使用时,编译器将优先使用具体化程度最高的模板:

Pair<double, double> p1;     //使用通用模板
Pair<double, int> p1;        //使用部分具体化模板
Pair<int, int> p1;           //使用显式具体化模板

7.成员模板

示例:

// tempmemb.cpp -- template members
#include <iostream>
using std::cout;
using std::endl;

template <typename T>
class beta
{
private:
    template <typename V>  // nested template class member
    class hold
    {
    private:
        V val;
    public:
        hold(V v  = 0) : val(v) {}
        void show() const { cout << val << endl; }
        V Value() const { return val; }
    };
    hold<T> q;             // template object
    hold<int> n;           // template object
public:
    beta( T t, int i) : q(t), n(i) {}
    template<typename U>   // template method
    U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
    void Show() const { q.show(); n.show();}
};

int main()
{
    beta<double> guy(3.5, 3);
    cout << "T was set to double\n";
    guy.Show();
    cout << "V was set to T, which is double, then V was set to int\n";
    cout << guy.blab(10, 2.3) << endl;
    cout << "U was set to int\n";
    cout << guy.blab(10.0, 2.3) << endl;
    cout << "U was set to double\n";
    cout << "Done\n";
    // std::cin.get();
    return 0; 
}

 (1)该模板类将另一个模板类作为其成员:

template <typename T>
class beta
{
private:
    template <typename V>  
    class hold
    {

    }
}

因为 hold 模板是放在私有部分的,所以只能在 beta 类中访问,beta 类使用 hold 模板声明了两个数据成员:

hold<T> q;             // template object
hold<int> n;           // template object

(2)该模板类将另一个模板函数作为其成员:

template <typename T>
class beta
{
public:
    template<typename U>  
    U blab(U u, T t) 
        { 
            return (n.Value() + q.Value()) * u / t; 
        }
}

可以看到,现在整个模板一共有三个参数:T、V、U

(3)调用

在main函数中,下述方法将 T 设置为 double :

beta<double> guy(3.5, 3);

 下述方法调用的第一个参数将 U 类型设置为 int:

guy.blab(10, 2.3)

  下述方法 V 类型设置为 T 类型,也即 double:

guy.Show();

 因为 show() 将调用 q.show(),q 在实例化为  hold<double> q 时会将 V 设置为 double;

同理,下述方法 V 类型设置为 double 类型:

hold<int> n;

8.将模板用作参数(略)

9.模板类和友元(略)

10.模板别名(略)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值