C++ Primer Plus 书之--C++ 模板类

定义模板类

先看一个之前自定义的Stack类

// 别名
typedef unsigned long Item;

class Stack
{
private:
	enum {MAX = 10};
	Item items[MAX];
	int top;
	
public:
	Stack();
	bool isempty() const;
	bool isfull() const;
	bool push(const Item & item);
	bool pop(Item & item);
};

模板类以下面这样的代码开头:

template <class Type>

关键字template告诉编译器, 将要定义一个模板, 尖括号中的内容相当于函数的参数列表. 可以把关键字class看作是变量的类型名, 该变量接收类型作为其值, 把Type看做是该变量的名称.

这里的class并不意味着Type必须是一个类, 而只是表明Type是一个通用的类型说明符, 在使用模板时, 将使用实际的 类型替换它, 也可以使用typename替换class:

template <typename Type>

同样, 可以使用模板成员函数替换原有类的类方法. 每个函数头都将以相同的模板声明打头:

template <class Type>

同样应使用泛型名Type(可以替换成T等其他字符)替换typedef标识符Item, 另外, 还需要将类限定符从Stack::改为Stack<Type>::, 例如:

bool Stack::push(const Item & item)
{
	...
}

应改为:

template <class Type>
bool Stack<Type>::push(const Type & item)
{
    ...
}

如果在类声明中定义了方法(内联定义), 则可以省略模板前缀和类限定符.

模板的具体实现--如:用来处理string对象的栈类被称为实例化(instantiation)或具体化(specialization). 不能将模板成员函数放在独立的实现文件中. 由于模板不是函数, 他们不能单独编译, 模板必须与特定的模板实例化请求一起使用. 因此最简单的方法是将所有模板信息放在一个头文件中, 并在要使用这些模板的肯建中包含该头文件:

看一个完整的例子:

// stacktp.h
#ifndef STACKTP_H_
#define STACKTP_H_

// 模板类
template <class Type>
class Stack
{
private:
	enum {MAX = 10};
	Type items[MAX];
	int top;
public:
	Stack();
	bool isempty();
	bool isfull();
	bool push(const Type & tiem);
	bool pop(Type & 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

使用Stack模板类:

// 创建一个int型的Stack
Stack<int> kernels;

// 创建一个string类型的Stack对象
Stack<string> colonels;

调用模板类的文件: 

// 使用Stack模板类
// stacktem.cpp
#include <iostream>
#include <string>
#include <cctype>
#include "stacktp.h"

using std::cin;
using std::cout;
using std::endl;

int main()
{
	Stack<std::string> st;
	char ch;
	std::string po;
	cout << "Please enter A to add a purchase order, " << endl;
	cout << "P to process a PO, or Q to quit." << endl;
	
	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" << endl;
				else 
					st.push(po);
				break;
				
			case 'p':
			case 'P':
				if(st.isempty())
					cout << "stack already empty" << endl;
				else {
					st.pop(po);
					cout << "PO #" << po << " popped" << endl;
				}
				break;
		}
		cout << "Please enter A to add a purchase order, P to process a PO, or Q to quit" << endl;
	}
	cout << "Bye" << endl;
	return 0;
}

程序运行结果为:

 

数组模板示例和非类型参数

允许指定数组大小的简单数组模板.
我们可以使用模板参数来提供常规数组的大写, 例如:

// arraytp.h
#ifndef ARRAYTP_H_
#define ARRAYTP_H_

#include <iostream>
#include <cstdlib>

// 指出是一个模板类
// class指出T为类型参数, 而int指出n的类型为int, 这种参数(指定特殊的类型而不是用作泛型名, 例如这里的int)称为非类型或表达式参数.
template <class T, int n>
class ArrayTP
{
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 << "Error in array limits: " << i << " is out of range" << endl;
		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 << "Error in array limits: " << i << " is out of range" << endl;
		std::exit(EXIT_FAILURE);
	}
	return ar[i];
}

#endif;

假设有如下的声明:

ArrayTP<double, 12> egg;

这将导致编译器定义名为ArrayTP<double, 12>的类, 并创建一个类型为ArrayTP<double, 12>的egg对象. 定义类时, 编译器将使用double替换T, 使用12替换n
表达式参数可以使整型, 枚举, 引用或指针, 因此double m是不合法的, 但double * rm是合法的. 另外模板代码不能修改参数的值, 也不能使用参数的地址. 并且实例化模板时, 用作表达式参数的值必须是常量表达式.
表达式参数方法的主要缺点是, 每种数组大小都将生成自己的模板. 例如:

ArrayTP<double, 12> eggs;
ArrayTP<double, 13> donuts;


上面的声明将生成两个独立的类声明 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值