模板提供参数化类型,即能够将类型名作为参数传递给接收方来建立类或函数。
#pragma once
//stacktp.h -- a stack template
#ifndef STACKTPH
#define STACKTPH
//声明
template <class Type> //或者tmplate <typename Type>
class Stack
{
private:
enum{MAX=10};
Type itemsMAX;
int top;
public:
Stack();
bool isempty();
bool isfull();
bool push(const Type& item);
bool pop(Type& item);
//如果在类声明中定义了⽅法(内联定义),则可以省略模板前缀和类限定符。
};
//实现
template <class Type> //使用模板成员函数替换原有类的方法,每个函数头以template <class Type>声明打头;
Stack<Type>::Stack() //同样应使⽤泛型名Type替换typedef标识符Item。另外,还需将类限定符从Stack::改为Stack<Type>::
{
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)
{
itemstop++ = item;
return true;
}
else
{
return false;
}
}
template <class Type>
bool Stack<Type>::pop(Type& item)
{
if (top > 0)
{
item = items--top; //根据push的实现可知栈顶top指向的位置是没有数据的,所以使用--top
return true;
}
else
{
return false;
}
}
#endif // STACKTPH
模板的具体实现——如⽤来处理string对象的栈类—— 被称为实例化(instantiation)或具体化(specialization)。
```c++
Stack<int> kernels;
Stack<string> colonels;
//编译器将按Stack<Type>模板来⽣成两个独⽴的类声明和两组独⽴的类⽅法。
//类声明Stack<int>将使⽤int替换模板中所有的Type
//类声明Stack<string>将⽤string替换Type。
```
//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;
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.
A
Enter a PO number to add:red
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
A
Enter a PO number to add:blue
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
A
Enter a PO number to add:silver
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
P
PO #silverpopped
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
P
PO #bluepopped
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
P
PO #redpopped
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
可以将内置类型或类对象⽤作类模板Stack的类型。指针可以 吗?
例如,可以使⽤char指针替换程序清单14.14中的string对象吗?毕竟,这种指针是处理C-⻛格字符串的内置⽅式。
答案是可以创建指针栈,但如果不对程序做重⼤修改,将⽆法很好地⼯作。编译器可以创建 类,但使⽤效果如何就因⼈⽽异了。
切忌盲目使用模板
方法:
- 让调⽤程序提供⼀个指针数组,其中每个指针都指向不同的字符串。
- 注意,创建不同指针是调⽤程序的职责,⽽不是栈的职责。
- 栈的任务是管理指针,⽽不是创建指针。
可以在模板声明或模板函数定义内使⽤Stack;
在类的外⾯, 即**指定返回类型**或**使⽤作⽤域解析运算符**时,必须使⽤完整的 `Stack<Type>`。
- ⼀种⽅法是在类中使⽤**动态数组**和**构造函数参数**来提供元素数⽬;
- 另⼀种⽅法是使⽤**模板参数**来提供常规数组的⼤⼩,C++11新增的模板array就是这样做的。
```c++
//arraytp.h -- Array Template
#ifndef Arraytp_H_
#define Arraytp_H_
#include<iostream>
#include<cstdlib>
template<class T,int n> //使⽤**模板参数**来提供常规数组的⼤⼩
class ArrayTP //表达式参数int n可以是整型、枚举、引⽤或指针。因此,double m是不合法的,但double * rm和double * pm是合法的。
{
private:
T ar[n];
public:
ArrayTP() {};
explicit ArrayTP(const T& v);
virtual T& operator[](int i);
virtual T operator[](int i)const;
};
template<class T,int n>
ArrayTP<T, n>::ArrayTP(const T& v)
{
for (int i = 0; i < n; i++)
{
ar[i] = v;
}
}
template<class T, int n>
T& ArrayTP<T, n>::operator[](int i)
{
if (i < 0 || i >= n)
{
std::cerr << "Brror in array limits: " << i << "is out of range\n";
std::exit(EXIT_FAILURE);
}
return ar[i];
}
template<class T, int n>
T ArrayTP<T, n>::operator[](int i)const
{
if (i < 0 || i >= n)
{
std::cerr << "Brror in array limits: " << i << "is out of range\n";
std::exit(EXIT_FAILURE);
}
return ar[i];
}
#endif // !Arraytp_H_
```
- 关键字class(或在这种上下⽂中等价的关键字typename)指出T为**类型参数**,
- int指出n的类型为int,这种参数(指定特殊的类型⽽不是⽤ 作泛型名)称为⾮类型(non-type)或表达式(expression)参数。
- 模板代码**不能修改参数的值**,也**不能使⽤参数的地址**。所 以,在ArrayTP模板中**不能使⽤诸如n++和&n**等表达式。
- 实例化模板时,⽤作表达式参数的值**必须是常量表达式**。
- **表达式参数⽅法**使⽤的是为⾃动变量维护的内存栈。执⾏速度将更快,尤其是在使⽤了很多⼩型数组时。
- 表达式参数⽅法的主要缺点是,每种数组⼤⼩都将⽣成⾃⼰的模 板。也就是说,下⾯的声明将⽣成两个独⽴的类声明:
- ```c++
ArrayTP<double,12> eggweights;
ArrayTP<double,13> donuts;
```
- 构造函数⽅法使⽤的是通过new和delete管理的堆内存;
- 下⾯的声明只⽣成⼀个类声明,并将数组⼤⼩信息传递给类的构 造函数:
- ```c++
Stack<int> eggs(12);
Stack<int> dunkers(13);
```
- 另⼀个区别是,构造函数⽅法更通⽤,这是因为数组⼤⼩是作为**类成员**(⽽不是硬编码)存储在定义中的。这样可以将⼀种尺⼨的数组赋给另⼀种尺⼨的数组,也可以创建允许数组⼤⼩可变的类。
可以将⽤于常规类的技术⽤于模板类。
template <typename T>
class Array
{
private:
T entry;
...
public:
...
};
template <typename Type>
class GrowArray:public Array<Type>{...};
template <typename Tp>
class Stack
{
Array<tp>ar;
...
};
...
Array<Stack<int>> asi; //C++98要求使⽤⾄少⼀个空⽩字符将两个>符号分开,以免与运算符>>混淆。C++11不要求这样做。
- 在模板语法中,维的顺序与等价的⼆维数组相反。
- 递归使用例子:
```c++
ArrayTP<ArrayTP<int,5>,10> twodee;
//这使得twodee是⼀个包含10个元素的数组,其中每个元素都是⼀个包含5个int元素的数组。
//与之等价的常规数组声明如下:
int twodee[10][5];
```
例子:
```c++
//twod.cpp -- making a 2-d array
#include<iostream>
#include"arraytp.h"
int main(void)
{
using std::cout;
using std::endl;
ArrayTP<int, 10> sums;
ArrayTP<double, 10> aves;
ArrayTP<ArrayTP<int, 5>, 10> twodee;
int i, j;
for (i = 0; i < 10; i++)
{
sums[i] = 0;
for (j = 0; j < 5; j++)
{
twodee[i][j] = (i + 1) * (j + 1);
sums[i] += twodee[i][j];
}
aves[i] = (double)sums[i] / 10;
}
for ( i = 0; i < 10; i++)
{
for (j = 0; j < 5; j++)
{
cout.width(2);
cout << twodee[i][j] << ' ';
}
cout << ": sum = ";
cout.width(3);
cout << sums[i] << ", average = " << aves[i] << endl;
}
cout << "Done.\n";
return 0;
}
```
运行结果:
```c++
1 2 3 4 5 : sum = 15, average = 1.5
2 4 6 8 10 : sum = 30, average = 3
3 6 9 12 15 : sum = 45, average = 4.5
4 8 12 16 20 : sum = 60, average = 6
5 10 15 20 25 : sum = 75, average = 7.5
6 12 18 24 30 : sum = 90, average = 9
7 14 21 28 35 : sum = 105, average = 10.5
8 16 24 32 40 : sum = 120, average = 12
9 18 27 36 45 : sum = 135, average = 13.5
10 20 30 40 50 : sum = 150, average = 15
Done.
```
- 模板可以包含多个类型参数。
- 假设希望类可以保存两种值, 则可以创建并使⽤**Pair模板**来保存**两个不同的值**(标准模板库提供了类 似的模板,名为pair)。
例子:
```c++
//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>{"Duck",5},
Pair<string,int>{"Fresco",4},
Pair<string,int>{"Cafe",5},
Pair<string,int>{"Eats",3}
};
int joints = sizeof(ratings) / sizeof(Pair<string, int>);
cout << "Rating:\t Eatery\n";
for (int i = 0; i < joints; i++)
{
cout << ratings[i].second() << ":\t"
<< ratings[i].first() << endl;
}
cout << "Oops! Resvised rating:\n";
ratings[3].first() = "Fab";
ratings[3].second() = 6;
cout << ratings[3].second() << ":\t"
<< ratings[3].first() << endl;
return 0;
}
```
运行结果:
```c++
Rating: Eatery
5: Duck
4: Fresco
5: Cafe
3: Eats
Oops! Resvised rating:
6: Fab
```
- 在main( )中必须使⽤ Pair来调⽤构造函数,并将它作为sizeof的参数。这是因为类 名是Pair,⽽不是Pair。
- Pair是另⼀个完全不同的类的名称。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。