zoukankan      html  css  js  c++  java
  • 《C++ Primer Plus》14.4 类模板 学习笔记

    14.4.1 定义类模板
    下面以第10章的Stack类为基础来建立模板。原来的类声明如下:
    typedef unsigned long Item;

    class Stack
    {
    private:
        enum {MAX = 10};    // constant specific to class
        Item items[MAX];    // holds stack items
        int top;            // index for top stack item
    public:
        Stack();
        bool isempty() const;
        bool isfull() const;
        // push() returns false if stack already is full, true otherwise
        bool push(const Item & item);   // add item to stack
        // pop() returns false if stack already is empty, true otherwise
        bool pop(Item & item);          // pop top into item
    };
    采用模板时,将使用模板定义替换Stack声明,使用模板成员函数替换Stack的成员函数。和模板函数一样,模板类以下面这样的代码开头:
    template <class Type>
    关键字template告诉编译器,将要定义一个模板。尖括号中的内容相当于函数的参数列表。可以把关键字class看作是变量的类型名,该变量接受类型作为其值,把Type看作是改变量的名称。
    这里使用class并不意味着Type必须是一个类;而只是表明Type是一个通用的类型说明符,在使用模板时,将使用实际的类型替换它。较新的C++实现允许在这种情况下使用不太容易混淆的关键字typename代替class:
    template <typename Type>    // new choice
    可以使用自己的泛型名代替Type,其命名规则与其他标识符相同。当前比较流行的选项包括T和Type。在模板定义中,可以使用泛型名来标识要存储在栈中的类型。对于Stack来说,这意味着将声明中所有的typedef标识符Item替换为Type。例如,
    Item items[MAX];    // holds stack items
    应改为:
    Type items[MAX];    // holds stack items
    同样,可以使用模板成员函数替换原有类的类方法。每个函数头都将以相同的模板声明打头:
    template <class Type>
    同样应使用泛型名Type替换typedef标识符Item。另外,还需要将限定符从Stack::改为Stack<Type>::。例如,
    bool Stack::push(const Item & item)
    {
    ...
    }
    应改为:
    template <class Type>       // or template <typename Type>
    bool Stack<Type>::push(const Type & item)
    {
    ...
    }
    如果在类声明中定义了方法(内联定义),则可以省略模板前缀和类限定符。
    程序清单14.13列出了类模板和成员函数。知道这些模板不是类和成员函数定义至关重要。它们是C++编译器指令,说明了如何生成类和成员函数定义。模板的具体是现——如用来处理string对象的栈类——被称为实例化(instantiation)或具体化(specialization)。不能将模板成员函数放在独立的实现文件中。由于模板不是函数,它们不能单独编译。模板必须与特定的模板实例化请求一起使用。为此,最简单的方法是将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含该文件头。
    程序清单14.13 stacktp.h

    // 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 // STACKTP_H_


    14.4.2 使用模板类
    仅在程序包含模板并不能生成模板类,而必须请求实例化。为此,需要生命一个类型为模板类的对象,方法是使用所需的具体类型替换泛型名。例如,下面的代码创建两个栈,一个用于存储int,另一个用于存储string对象:
    Stack<int> kernels;        // create a stack of ints
    Stack<string> colonels;        // create a stack of string objects
    看到上述声明后,编译器将按Stack<Type>模板来生成两个独立的类声明和两组独立的类方法。
    程序清单14.14修改了原来的栈测试程序(程序清单11.12),使用字符串而不是unsigned long值作为订单ID。
    程序清单14.14 stacktem.cpp

    // 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.
    "
             << "P to process a PO, or Q to quit.
    ";
        while (cin >> ch && std::toupper(ch) != 'Q')
        {
            while (cin.get() != '
    ')
                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
    ";
                            else
                                st.push(po);
                            break;
                case 'P':
                case 'p':   if (st.isempty())
                                cout << "stack already empty
    ";
                            else {
                                st.pop(po);
                                cout << "PO #" << po << " popped
    ";
                                break;
                            }
            }
            cout << "Please enter A to add a purchase order.
    "
                 << "P to process a PO, or Q to quit.
    ";
        }
        cout << "Bye
    ";
        return 0;
    }

    效果:

    Please enter A to add a purchase order.
    P to process a PO, or Q to quit.
    A
    Enter a PO number to add: moonlightpoet
    Please enter A to add a purchase order.
    P to process a PO, or Q to quit.
    A
    Enter a PO number to add: moonlit
    Please enter A to add a purchase order.
    P to process a PO, or Q to quit.
    P
    PO #moonlit popped
    Please enter A to add a purchase order.
    P to process a PO, or Q to quit.
    P
    PO #moonlightpoet popped
    Please enter A to add a purchase order.
    P to process a PO, or Q to quit.
    P
    stack already empty
    Please enter A to add a purchase order.
    P to process a PO, or Q to quit.
    Q
    Bye

     
    可以通过如下方法声明一个允许指定数组大小的简单数组模板:
    template <class T, int n>
    class ArrayTP
    {
    ...
    };
    请注意class(或在这种上下文中等价的关键字typename)指出T为类型参数,int指出n的类型为int。这种参数(指定特定的类型而不是用作泛型名)称为非类型(non-type)或表达式(expression)参数。假设有下面的声明:
    ArrayTP<double, 12> eggweights;
    这将导致编译器定义名为ArrayTP<double, 12>的类,并创建一个类型为ArrayTP<double, 12>的eggweight对象。定义类时,编译器将使用double替换T,使用12替换n。

    14.4.5 模板多功能性
    可以将用于常规类的技术用于模板类。模板类可用作基类,也可用作组件类,还可用作其他模板的类型参数。例如,可以使用数组模板实现栈模板,也可以使用数组模板来构造数组——数组元素是基于栈模板的栈。即可以编写下面的代码:
    template <typename T>   // or <class T>
    class Array
    {
    private:
        T entry;
        ...
    };

    template <typename Tp>
    class Stack
    {
        Array<Tp> ar;   // use an Array<> as a component
    };
    ...
    Array < Stack<int> > asi;   // an array of stacks of int
    在最后一条语句中,C++98要求使用至少一个空白字符将两个>符号分开,以免与运算符>>混淆。C++11不要求这样做。
    1.递归使用模板
    可以递归使用模板。例如,对于前面的数组模板定义,可以这样使用它:
    ArrayTP < ArrayTP<int,5>, 10> twodee;
    2.使用多个类型参数
    模板可以包含多个类型参数。例如,假设希望类可以保存两种值,则可以创建并使用Pair模板来保存两个不同的值(标准模板库提供了类似的模板,名为pair)。程序清单14.19所示的小程序是一个这样的示例。其中,方法first() const和second() const报告存储的值,由于这两个方法返回Pair数据成员的引用,因此让您能够通过赋值重新设置存储的值。
    程序清单14.19 pairs.cpp

    // 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:	 Eatery
    ";
        for (int i = 0; i < joints; i ++)
            cout << ratings[i].second() << ":	"
                 << ratings[i].first() << endl;
        cout << "Oops! Revised rating:
    ";
        ratings[3].first() = "Bertie's Fab Eats";
        ratings[3].second() = 6;
        cout << ratings[3].second() << ":	 "
             << ratings[3].first() << endl;
        return 0;
    }

    输出如下:

    Rating:	 Eatery
    5:	The Purpled Duck
    4:	Jaquie's Frisco Al Fresco
    5:	Cafe Souffle
    3:	Bertie's Eats
    Oops! Revised rating:
    6:	 Bertie's Fab Eats

     3.默认类型模板参数
    类模板的另一项新特性是,可以为类型参数提供默认值:
    template <class T1, class T2 = int> class Topo {...};
    这样,如果省略T2的值,编译器将使用int:
    Topo<double, double> m1;    // T1 is double, T2 is double
    Topo<double> m2;        // T1 is double, T2 is int

    14.4.6 模板的具体化
    *** 隐式实例化、显式实例化和显式具体化。……

    14.4.7 成员模板
    ……

    14.4.8 将模板用作参数
    模板可以包含类型参数(如typename T)和非类型参数(如int n)。模板还可以包含本身就是模板的参数,这种参数是模板新增的特性,用于实现STL。
    template <template <typename T> class Thing>
    class Crab
    模板参数是template <typename T> class Thing,其中template <typename T> class 是类型,Thing是参数。

    14.4.9 模板类和友元
    ……

    14.4.10 模板别名
    template<typename T>
      using arrtype = std::arrat<T,12>;    // template to create multiple aliases
    这将arrtype定义为一个模板别名,可使用它来指定类型,如下所示:
    arrtype<double> gallons;    // gallons is type std::array<double, 12>
    arrtype<int> days;        // days is type std::array<int, 12>
    arrtype<std::string> months;    // months is type std::array<std::string, 12>
    C++11允许将语法using=用于非模板。用于非模板时,这种语法与常规typedef等价:
    typedef const char * pc1;
    using pc2 = const char *;
    typedef const int *(*pa1)[10];
    using ps2 = const int *(*)[10];

  • 相关阅读:
    Informix日期获取上周上月昨天去年SQL
    Oracle-创建一个DBLink的方法
    Kafka-Partitions与Replication Factor 调整准则
    Linux-删除文件空间不释放问题解决
    Redhat7-Oracle-sqlldr-安装配置
    Centos7-安装oracle客户端11.2.0.4
    Centos7-单机安装jumpserver
    Redhat6.4-yum本地源安装配置
    Linux-zip unzip 命令日常使用
    xxl-job日志
  • 原文地址:https://www.cnblogs.com/moonlightpoet/p/5669239.html
Copyright © 2011-2022 走看看