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 

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

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

template 

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

template 

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

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

应改为:

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

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

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

看一个完整的例子:

// stacktp.h
#ifndef STACKTP_H_
#define STACKTP_H_

// 模板类
template 
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 
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

使用Stack模板类:

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

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

调用模板类的文件: 

// 使用Stack模板类
// stacktem.cpp
#include 
#include 
#include 
#include "stacktp.h"

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

int main()
{
	Stack 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;
}

程序运行结果为:

C++ Primer Plus 书之--C++ 模板类_第1张图片

 

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

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

// arraytp.h
#ifndef ARRAYTP_H_
#define ARRAYTP_H_

#include 
#include 

// 指出是一个模板类
// class指出T为类型参数, 而int指出n的类型为int, 这种参数(指定特殊的类型而不是用作泛型名, 例如这里的int)称为非类型或表达式参数.
template 
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
ArrayTP::ArrayTP(const T & v)
{
	for(int i = 0; i < n; i++)
	{
		ar[i] = v;
	}
}

template
T & ArrayTP::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
T ArrayTP::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 egg;

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

ArrayTP eggs;
ArrayTP donuts;


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

 

 

你可能感兴趣的:(C++)