博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《C++ Primer Plus》14.4 类模板 学习笔记
阅读量:6804 次
发布时间:2019-06-26

本文共 8156 字,大约阅读时间需要 27 分钟。

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 Stack{private: enum {MAX = 10}; // constant specific to class Type items[MAX]; // holds stack items int top; // index for top stack itempublic: Stack(); bool isempty(); bool isfull(); bool push(const Type & item); // add item to stack bool pop(Type & item); // pop top into item};template
Stack
::Stack(){ top = 0;}template
bool Stack
::isempty(){ return top == 0;}template
bool Stack
::isfull(){ return top == MAX;}template
bool Stack
::push(const Type & item){ if (top < MAX) { items[top++] = item; return true; } else return false;}template
bool Stack
::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 
#include
#include
#include "stacktp.h"using std::cin;using std::cout;int main(){ Stack
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"; return 0;}

效果:

Please enter A to add a purchase order.P to process a PO, or Q to quit.AEnter a PO number to add: moonlightpoetPlease enter A to add a purchase order.P to process a PO, or Q to quit.AEnter a PO number to add: moonlitPlease enter A to add a purchase order.P to process a PO, or Q to quit.PPO #moonlit poppedPlease enter A to add a purchase order.P to process a PO, or Q to quit.PPO #moonlightpoet poppedPlease enter A to add a purchase order.P to process a PO, or Q to quit.Pstack already emptyPlease enter A to add a purchase order.P to process a PO, or Q to quit.QBye

 

可以通过如下方法声明一个允许指定数组大小的简单数组模板:
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 
#include
template
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
T1 & Pair
::first(){ return a;}template
T2 & Pair
::second(){ return b;}int main(){ using std::cout; using std::endl; using std::string; Pair
ratings[4] = { Pair
("The Purpled Duck", 5), Pair
("Jaquie's Frisco Al Fresco", 4), Pair
("Cafe Souffle", 5), Pair
("Bertie's Eats", 3) }; int joints = sizeof(ratings) / sizeof (Pair
); 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; return 0;}

输出如下:

Rating:	 Eatery5:	The Purpled Duck4:	Jaquie's Frisco Al Fresco5:	Cafe Souffle3:	Bertie's EatsOops! 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];

转载地址:http://arjwl.baihongyu.com/

你可能感兴趣的文章
shell 数组
查看>>
Linux操作系统的安装
查看>>
服务器RAID
查看>>
python函数是引用传递(对可变对象而言)
查看>>
Cisco ASA防火墙简易配置与调试手册
查看>>
Node.js搭建聊天室
查看>>
Rob Pike的5个编程原则
查看>>
Expression Blend实例中文教程(7) - 动画基础快速入门Animation
查看>>
《第一行代码》1day~了解全貌
查看>>
复习javaIO 之File类
查看>>
How to install snmpwalk snmpget on CentOS 6.4 6.3 5.9 Redhat RHEL Fedora
查看>>
最小生成树
查看>>
Mybatis中配置Mapper的方法
查看>>
Java基础学习总结(19)——Java环境变量配置
查看>>
Mvc5+Entity Framework6 之二----在MVC中用Entity Framework实现基本的CRUD
查看>>
我的友情链接
查看>>
大型网站技术架构(四)网站的高性能架构
查看>>
linux系统修改SSH最大连接数,修改nofile,nproc参数方法
查看>>
Hadoop-2.5.2集群安装配置详解
查看>>
解决报表网页版转成excel时,首位0被清除的问题
查看>>