欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C++ chapter 10——模板

程序员文章站 2022-03-25 07:51:19
**模板的概念 函数模板 类模板 名空间** 一、模板的概念 c++的模板提供对逻辑结构相同的数据对象通用行为的定义。模板运算对象的类型不是实际的数据类型,而是一种参数化的类型。 一个带类型参数的函...

**模板的概念
函数模板
类模板
名空间**

一、模板的概念
c++的模板提供对逻辑结构相同的数据对象通用行为的定义。模板运算对象的类型不是实际的数据类型,而是一种参数化的类型。
一个带类型参数的函数称为函数模板,一个带类型参数的类称为类模板。

二、函数模板

1、函数模板的概念
函数模板的基本原理是通过数据类型的参数化,将一组算法相同但所处理数据类型不同的重载函数凝练成一个函数模板。编译时,再由编译器按照函数模板自动生成针对不同数据类型的重载函数定义代码。
使用函数模板。对于函数模板,数据类型本身成了它的参数。

*2、函数模板的说明*
**格式:**template < typename t1, typename t2,… typename tn>

template—模板说明关键字
typename—类型参数指定关键字(也可以是class)
t1,t2,…tn—类型参数,是用户定义标识符,可实例化为任何内部类型或用户定义类型。

3、函数模板与模板函数
函数模板定义由模板说明和函数定义组成。
格式为:
template < typename t1,typename t2,…typename tn>
<返回值类型> <函数名>(模板函数形参表)
{
//函数定义体
}
所有在模板说明的类型参数必须在函数定义中至少出现一次。函数参数表中可以使用类型参数,也可以使用一般参数。
注意:
(1)函数模板只是说明,不能直接执行,需要实例化为模板函数后才能执行。
(2)模板的类型参数由调用它的实际参数的具体数据类型替换,由编译器生成一段真正可执行的代码。这个过程称为实例化。
(3)一个函数模板用类型实例化后,称为模板函数

//eg10-1:用函数模板输出不同类型的数组
#include 
using namespace std;

template   //定义函数模板
void outputarray(const t *array, int count) {
    for (int i = 0; i < count; i++)
        cout << array[i] << " ";
    cout << endl;
}

int main() {
    const int a_count = 8, b_count = 8, c_count = 20;
    int a[a_count] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    double b[b_count] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 };
    char c[c_count] = "welcome!";

    cout << " a array contains:" << endl;
    outputarray(a, a_count);
    cout << " b array contains:" << endl;
    outputarray(b, b_count);
    cout << " c array contains:" << endl;
    outputarray(c, c_count);
    return 0;
}

4、函数模板的优点
(1)函数模板方法克服了c语言用大量不同函数名表示相似功能的坏习惯;
(2)克服了宏定义不能进行参数类型检查的弊端;
(3)克服了c++函数重载用相同函数名字重写几个函数的繁琐。
因而,函数模板是c++中功能最强的特性之一,具有宏定义和重载的共同优点,是提高软件代码重用率的重要手段。

//eg10-2:用函数模板实现对n个元素的数组求最小值
#include 
using namespace std;

template 
t min(t a[], int n)
{
    int i;
    t minv = a[0];
    for (i = 1; ia[i])
            minv = a[i];
    return minv;
}
int main()
{
    int a[] = { 1, 3, 0, 2, 7, 6, 4, 5, 2 };
    double b[] = { 1.2, -3.4, 6.8, 9, 8 };
    cout << "a数组的最小值为:" << min(a, 9) << endl;
    cout << "b数组的最小值为:" << min(b, 4) << endl;
    return 0;
}
//eg10-3:求两个不同类型数据的较大值
#include 
using namespace std;

template 
t1 max(t1 x, t2 y)
{
    return x>(t1)y ? x : (t1)y;
}
int main()
{
    int i = 10, j = 56;
    float x1 = 50.34, x2 = 56.34;
    double y1 = 673.36, y2 = 465.972;
    cout << "the max of i,x1 is:" << max(i, x1) << endl;
    cout << "the max of x1,y1 is:" << max(x1, y1) << endl;
    cout << "the max of j,y2 is:" << max(j, y2) << endl;
    return 0;
}

5、重载函数模板
函数模板与普通函数一样,也可以重载。可以定义同名的函数模板,提供不同的参数和实现;也可以用其他非模板函数重载。

(1)函数模板的重载

重载函数模板便于定义类型参数,或者函数模板参数的类型、个数不相同所进行的类似操作。
例如,例10-3的max函数模板可以重载为求数组最大元素的函数模板。

//例10-4:函数模板重载
#include "iostream"
using namespace std;

template 
t max(t x, t y)
{
    return (x>y) ? x : y;
};

template 
t max(t a[], int n)
{
    t temp;
    int i;
    temp = a[0];
    for (i = 1; itemp)
            temp = a[i];
    return temp;
}

int main()
{
    int i = 10, j = 56;
    float x1 = 50.34, x2 = 56.34;
    double y1 = 673.36, y2 = 465.972;
    double x[10] = { 1.2, 3.4, 8.9, 6.5, 9.3, 15.8, 3.8, 21.3, 11.2, 2.1 };
    cout << "the max of i,j is:" << max(i, j) << endl;
    cout << "the max of x1,x2 is:" << max(x1, x2) << endl;
    cout << "the max of y1,y2 is:" << max(y1, y2) << endl;
    cout << "the max element of x is:" << max(x, 10) << endl;
    return 0;
}

(2)用普通函数重载函数模板
函数模板实例化时,实际参数类型替换类型参数。虽然这种参数替换具有类型检查功能,却没有普通函数传值参数的类型转换机制。

例如:
对函数模板
template
t max(t x,t y)
{
return (x>y)?x:y;
};
调用时对应的两个实际参数必须类型相同。若有:
int k;char c;
则调用max(k,c); 将出现语法错误。为了解决这一问题,可以用非模板函数重载一个同名的函数模板。

//例10-5:用普通函数重载函数模板
#include 
using namespace std;
template 
t max(t x, t y)
{
    return (x>y) ? x : y;
};
int max(char a, int b)   //函数重载
{
    return(a>b ? a : b);
}
int main()
{
    int k = 98;
    char c = 'a';
    double y1 = 673.36, y2 = 465.972;
    cout << "the max of k, c is : " << max(k, c) << endl;  //调用正确
    cout << "the max of c, k is : " << max(c, k) << endl;  //调用正确
    cout << "the max of y1,y2 is:" << max(y1, y2) << endl;
    return 0;
}

注意:
定义重载函数要注意避免二义性。
例如,在例10-5中,如果重载函数改为:
int max(int a,int b)
{ return a>b?a:b;}
将出现二义性,在调用时无法正确匹配。
max(k,k);
//错误,不知调用t max(t,t)还是max(int,int)?
max(c,c); //错误

编译器调用函数的匹配规则:

(1)寻找和使用最符合函数名和参数类型的函数,若找到则调用它。
(2)否则,寻找一个函数模板,将其实例化产生一个匹配的模板函数,若找到则调用它。
(3)否则,寻找可以通过类型转换进行参数匹配的重载函数,若找到则调用它。

如果按以上步骤均未能找到匹配函数,则这个调用是错误的。
如果这个调用有多于一个的匹配选择,则调用匹配出现二义性,也是错误的。

在调用函数模板的程序员看来,函数模板与普通函数没有什么区别。唯一不同的是,函数模板 就像是一个具有通用类型的函数,可以处理不同类型的数据。

三、类模板

(1)应用模板技术,可以将一组功能相同但所处理数据类型不同的类凝练成一个类模板。编译时,再由编译器按照类模板自动生成针对不同数据类型的类定义代码。
(2)使用类模板可以为类声明一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值,能取任意类型(包括基本类型和用户自定义类型)。
(3)类模板与函数模板类似,它可以为各种不同的数据类型定义一种模板,在引用时使用不同的数据类型实例化该类模板,从而形成一个类的集合。

1、类模板与模板类
类模板由模板说明和类说明构成。格式为:
template
class 类名 { 类声明体 };
template < typename type >
返回类型 类名<类型名表>::成员函数名1(形参表)
{ 成员函数1定义体 }

template
返回类型 类名<类型名表>::成员函数名n(形参表)
{ 成员函数n定义体 }

//例10-6:用类模板来处理任意类型的链表
#include 
using namespace std;

template   //声明模板
struct node         //定义结构体类型模板
{
    t val;          //val取任意类型,即模板参数类型
    node *next;
};

template    //声明模板
class list           //定义类模板
{
    node  *head;  //此处node为结构体类型模板
    int size;
public:
    list(){ head = 0; size = 0; }
    bool insert(t val);    //参数取t类型
    bool deletes(t val);   //参数取t类型
    bool contains(t val);  //参数取t类型
    void print();
    ~list();
};

template
list::~list()     //定义函数模板
{
    node  *temp;  //node为结构体类型模板
    for (node *p = head; p;)
    {
        temp = p;
        p = p->next;
        delete temp;
    }
}
template
bool list::insert(t x)
{
    node *nodes = new node;
    if (nodes)
    {
        nodes->val = x;
        nodes->next = head;
        head = nodes;
        size++;
        return true;
    }
    return false;
}

template 
bool list::deletes(t x)
{
    node *temp;
    if (head->val == x)
    {
        temp = head->next;
        delete head;
        size--;
        head = temp;
        return true;
    }
    for (node *p = temp = head; p; temp = p, p = p->next)
        if (p->val == x)
        {
            temp->next = p->next;
            delete p;
            size--;
            return true;
        }
    return false;
}
template
bool list::contains(t x)
{
    for (node *p = head; p; p = p->next)
        if (p->val == x)
            return true;
    return false;
}
template
void list::print()
{
    for (node *p = head; p; p = p->next)
        cout << p->val << " ";
    cout << endl;
}

void main()
{
    list intlist;  //定义一个整型链表对象intlist,此时传递进来的模板类型参数为
    intlist.insert(34);
    intlist.insert(54);
    intlist.insert(78);
    intlist.insert(76);
    intlist.insert(54);
    intlist.print();
    list floatlist; //定义一个浮点型链表对象floatlist,此时传递进来的模板类型参数为
    floatlist.insert(34.56);
    floatlist.insert(65.4);
    floatlist.insert(7534.56);
    floatlist.insert(364.56);
    floatlist.print();
    list charlist; //定义一个字符串链表对象charlist,此时传递进来的模板类型参数为
    charlist.insert("windows");
    charlist.insert("object");
    charlist.insert("borland");
    charlist.insert("virtual_function");
    charlist.print();
}

首先定义一个结构体模板,它与类模板用法相同;再定义一个类模板,结构体模板和类模板取同样的模板参数t。

说明:
(1)类模板的说明(包括成员函数定义)不是一个实实在在的类,只是对类的描述,称为类模板(class template)。类模板必须用类型参数将其实例化为模板类后,才能用来生成对象。一般地,其表示形式为:
类模板名 <类型实参表> 对象名(值实参表)
(2)其中<类型实参表>表示将类模板实例化为模板类时所用到的类型(包括固有的类型和用户已定义类型),<值实参表>表示将该模板类实例化为对象时其构造函数所用到的变量。一个类模板可以用来实例化多个模板类。
(3)类型参数必须至少在类说明中出现一次。
(4)类模板的成员函数都是函数模板,实现语法和函数模板类似。如果在类中定义(作为inline函数),不需要特别说明。如果在类外定义,则每个成员函数定义都要冠以模板参数说明,并且指定类名时要后跟类型参数。
(5)类模板在表示如数组、表、图等数据结构特别重要,因为这些数据结构的表示和算法不受所包含的元素类型的影响。