C++Primer Plus 第十四章代码重用:使用模板类
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:使用模板类
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
类模板14.4
继承(公有、私有或保护)和包含并不总是能够满足重用代码的需要。例如,Stack类(参见第 10章)和Queue类(参见第12章)都是容器类(container class),容器类设计用来存储其他对象或数据类型。例如,第 10 章的 Stack 类设计用于存储 unsigned long 值。可以定义专门用于存储 double 值或 string 对象的 Stack 类,
除了保存的对象类型不同外,这两种 Stack 类的代码是相同的。然而,与其编写新的类声明,不如编写一个泛型(即独立于类型的)栈,然后将具体的类型作为参数传递给这个类。这样就可以使用通用的代码生成存储不同类型值的栈。第10章的 Stack 示例使用typedef处理这种需求。然而,这种方法有两个缺点:首先,每次修改类型时都需要编辑头文件;其次,在每个程序中只能使用这种技术生成一种栈,即不能让typedef同时代表两种不同的类型,因此不能使用这种方法在同一个程序中同时定义 int 栈和 string 栈。C++的类模板为生成通用的类声明提供了一种更好的方法(C++最初不支持模板,但模板被引入后,就一直在演化,因此有的编译器可能不支持这里介绍的所有特性)。模板提供参数化(parameterized)类型,即能够将类型名作为参数传递给接收方来建立类或函数。例如,将类型名imt传递给Queue 模板,可以让编译器构造一个对 int进行排队的 Oueue 类。
C++库提供了多个模板类,本章前面使用了模板类 valarray,第4章介绍了模板类 vector 和 aray,而第16章将讨论的 C++标准模板库(STL)提供了几个功能强大而灵活的容器类模板实现。本章将介绍如何设计一些基本的特性。
14.4.1 定义类模板
下面以第10章的 Stack 类为基础来建立模板。原来的类声明如下:typedef unsigned long Item;
class Stack
{
private :
enumMAX=10;
Item items[MAX];
int top;
public:
Stack();
bool isempty()const;
bool isfull()const;
bool push(const Item &item);
bool pop(Item & item);
}
采用模板时,将使用模板定义替换 Stack声明,使用模板成员函数替换 Stack 的成员函数。和模板函数一样,模板类以下面这样的代码开头:
template <class Type>
关键字 template 告诉编译器,将要定义一个模板。尖括号中的内容相当于函数的参数列表。可以把关键字 class看作是变量的类型名,该变量接受类型作为其值,把Type 看作是该变量的名称。这里使用 class 并不意味着Type 必须是一个类;而只是表明Type 是一个通用的类型说明符,在使用模板时,将使用实际的类型替换它。较新的C++实现允许在这种情况下使用不太容易混淆的关键字typename代替class:
template <typename Type> //newer choice
可以使用自己的泛型名代替Type,其命名规则与其他标识符相同。当前流行的选项包括T和Type,我们将使用后者。当模板被调用时,Type将被具体的类型值(如imt或string)取代。在模板定义中,可以使用泛型名来标识要存储在栈中的类型。对于Stack 来说,这意味着应将声明中所有的typedef标识符 Item替换为 Type。例如,
Item items[MAX];//holds stack items
应改为:
Type items[MAX];//holds stack items
同样,可以使用模板成员函数替换原有类的类方法。每个函数头都将以相同的模板声明打头:
template 同样应使用泛型名Type 替换typedef标识符Item。另外,还需将类限定符从 Stack::改为 Stack.。例如,
bool Stack::push(const Item&item)
{
...
}
应该为:
template <class Type>
bool stack<Type>::push(const Type & item)
//or template <typename Type>
{
...
}
如果在类声明中定义了方法(内联定义),则可以省略板前缀和类限定符。程序清单 14.13列出了类模板和成员函数模板。知道这些模板不是类和成员函数定义至关重要。它们是C++编译器指令,说明了如何生成类和成员函数定义。模板的具体实现–如用来处理 string 对象的栈类–被称为实例化(instantiation)或具体化(specialization)。不能将模板成员函数放在独立的实现文件中(以前,C++标准确实提供了关键字export,让您能够将模板成员函数放在独立的实现文件中,但支持该关键字的编译器不多;C++11不再这样使用关键字export,而将其保留用于其他用途)。由于模板不是函数,它们不能单独编译。模板必须与特定的模板实例化请求一起使用。为此,最简单的方法是将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含该头文件。
程序清单 14.13stacktp.h
//stacktp.h -- a stack template #ifndef STACKTP_H_
#ifndef STACKTP_H_
#define STACKTP_H_
template <class Type>
class Stack
{
private:
enum { MAX = 10 }; // maximum stack size
Type 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 Type & item); // add item to stack
// pop() returns false if stack already is empty, true otherwise
bool pop(Type & item); // pop topinto item
};
template <class Type>
Stack<Type>::Stack()
{
top = 0;
}
template <class Type>
bool Stack<Type>::isempty() const
{
return top == 0;
}
template <class Type>
bool Stack<Type>::isfull() const
{
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模板来生成两个独立的类声明和两组独立的类方法。类声明 Stack将使用 int 替换模板中所有的Type,而类声明 Stack将用string替换Type。当然,使用的算法必须与类型一致。例如,Stack 类假设可以将一个项目赋给另一个项目。这种假设对于基本类型、结构和类来说是成立的(除非将赋值运算符设置为私有的),但对于数组则不成立。泛型标识符–例如这里的Type–称为类型参数(type parameter),这意味着它们类似于变量,但赋给它们的不能是数字,而只能是类型。因此,在kernel声明中,类型参数Type 的值为 int。注意,必须显式地提供所需的类型,这与常规的函数模板是不同的,因为编译器可以根据函数的参数类型来确定要生成哪种函数:
template
void simple(Tt){cout <<t<< ‘\n’;}
8 #
/generate void simple(int)simple(“two”);/generate void simple(const char *)
simple(2);
程序清单 14.14修改了原来的栈测试程序(程序清单 11.12),使用字符串而不是unsigned long 值作为订单 ID。
程序清单14.14 stacktem.cpp
//stacktem.cpp -- testing the template stack class
#include <iostream>
#include "stacktp.14.13.h"
#include <string>
#include <cctype>
#include <cstdlib>
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 && toupper(ch)!='Q')
{
while(cin.get()!='\n')
continue;
if(!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;
}
总结
提示:这里对文章进行总结:
1,模板类得讲解
2,下讲深入讲解模板类