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 = }; // 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 = ;
} template <class Type>
bool Stack<Type>::isempty()
{
return top == ;
} 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 > )
{
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.\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 ;
}

效果:

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[] =
{
Pair<string, int>("The Purpled Duck", ),
Pair<string, int>("Jaquie's Frisco Al Fresco", ),
Pair<string, int>("Cafe Souffle", ),
Pair<string, int>("Bertie's Eats", )
}; int joints = sizeof(ratings) / sizeof (Pair<string, int>);
cout << "Rating:\t Eatery\n";
for (int i = ; i < joints; i ++)
cout << ratings[i].second() << ":\t"
<< ratings[i].first() << endl;
cout << "Oops! Revised rating:\n";
ratings[].first() = "Bertie's Fab Eats";
ratings[].second() = ;
cout << ratings[].second() << ":\t "
<< ratings[].first() << endl;
return ;
}

输出如下:

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];

《C++ Primer Plus》14.4 类模板 学习笔记的更多相关文章

  1. 初步C++类模板学习笔记

    类模板 实现:在上课时间的定义给它的一个或多个参数,这些参数代表了不同的数据类型.                              -->抽象的类. 在调用类模板时, 指定參数, 由编 ...

  2. C++模板学习笔记

    一个有趣的东西:实现一个函数print, 输入一个数组, 输出数组的各个维度长度. eg. ], b[][], c[][][]; print(a); //(2, 4) print(b); //(3, ...

  3. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  4. c++类的学习笔记

    用结构体数据的安全性得不到保证. 使用类对数据进行封装,只能通过函数修改类中的数据 (1)类的定义 class 类名 { private: protected: public: }; private: ...

  5. python 面向对象(类)--学习笔记

    面向对象是一种编程方式, 主要集中在类和对象的两个概念 python 中的类符合封装, 继承, 多态的特征 类 是一个模板, 是n多函数的集成 对象 是类的实例化 类的成员分为三大类:字段.方法.属性 ...

  6. 初探C++类模版学习笔记

    类模板 实现:在定义类的时候给它一个或多个參数,这个些參数表示不同的数据类型.                              -->抽象的类. 在调用类模板时, 指定參数, 由编译系 ...

  7. C++Array类模板编写笔记

    C++Array类模板 函数模板和类模板都属于泛型技术,利用函数模板和类模板来创建一个具有通用功能的函数和类,以支持多种不同的形参,从而进一步简化重载函数的函数体设计. 声明方法:template&l ...

  8. 《C++ Primer Plus》14.2 私有继承 学习笔记

    C++(除了成员变量之外)还有另一种实现has-a关系的途径——私有继承.使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员.(如果使用保护继承,基类的公有成员和保护成员都将称为派生类的保 ...

  9. tornada模板学习笔记

    import tornado.web import tornado.httpserver import tornado.ioloop import tornado.options import os. ...

随机推荐

  1. Python3 Scrapy 安装方法

    Python3 Scrapy 安装方法 (一脸辛酸泪) 写在前面 最近在学习爬虫,在熟悉了Python语言和BeautifulSoup4后打算下个爬虫框架试试. 没想到啊,这坑太深了... 看了看相关 ...

  2. 手风琴式焦点图jQuery特效

    手风琴式焦点图jQuery特效是一款鼠标点击人物图像滑动切换案例说明信息代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div class="ag-cont ...

  3. 关于Java开发过程中质量提升-2自动化

    开发人员写代码过程中就实现自动代码检视,编辑器会提示编码规范错误,并给出正确实例,写完代码只需要提交到SVN库,然后启动自动化构建(可配置代码提交SVN后自动触发),测试环境中的项目马上和当前SVN库 ...

  4. [镜像]loop设备及losetup命令介绍

    最近需要对一个镜像文件进行修改,可以些方式是通过losetup和kpartx, mount完成,于是分享下面这篇 转自:http://blog.csdn.net/ustc_dylan/article/ ...

  5. Jackson2.1.4 序列化格式化时间

    public class User { private int id; private Date birthday; private double money; private String name ...

  6. 树莓派Raspberry命令行配置无线网络连接

    前言: 树莓派有多种联网的方式,通过有线网或者通过无线网.通过有线网连接是比较简单的,在开启dhcp的路由器下,直接插上网线就可以联网,本文介绍树莓派无线联网的方式.再没联网的情况下,如果没有屏幕等外 ...

  7. Iconfont在移动端遇到问题的探讨

    Iconfont越来越得到前端的关注,特别是阿里那个iconfont库的推出和不断完善,再加上连IE6都能兼容,的确是个好东西. 既然那么火,我们公司移动项目也计划加入这个iconfont,于是我就针 ...

  8. 修改Linux内核参数,减少TCP连接中的TIME-WAIT

    一台服务器CPU和内存资源额定有限的情况下,如何提高服务器的性能是作为系统运维的重要工作.要提高Linux系统下的负载能力,当网站发展起来之后,web连接数过多的问题就会日益明显.在节省成本的情况下, ...

  9. Thinkphp3.2版本Controller和Action的访问方法

    一.3.2版本以前controller和action的访问方式在3.2版本以前如果Controller=c.Action=a的话,访问规则如下:http://localhost:81/demo1/in ...

  10. 跑在Docker下的RHEL7编译Java8源码包

    1.运行Docker时需要加参数--cap-add=SYS_PTRACE,比如: docker run --cap-add=SYS_PTRACE --name buildjava8 -v /opt/r ...