C++ Primer Plus 书之--C++ 模板类深究1

模板类可以使用多个类型参数:

看一个例子:

// pairs.cpp
#include <iostream>
#include <string>

template <class T1, class T2>
class Pair
{
private:
	T1 a;
	T2 b;
public:
	// 这两个返回引用的实现虽然逻辑和后面的返回对象的实现是一样的
	// 但是不能删除, 如果删除后, 在下面的main方法中调用ratings[3].first() = "Berties"; 就会出现错误:
	// lvalue required as left operand of assignment 
	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>("first", 5),
		Pair<string, int>("second", 6),
		Pair<string, int>("third", 7),
		Pair<string, int>("four", 8)
	};
	
	int joints = sizeof(ratings) / sizeof(Pair<string, int>);
	cout << "Rating:\t Eatery " << endl;
	for(int i = 0; i < joints; i++)
	{
		cout << ratings[i].second() << ": \t " << ratings[i].first() << endl;
	}
	cout << "Oops! Revised rating: " << endl;
	ratings[3].first() = "Berties";
	ratings[3].second() = 9;
	cout << ratings[3].second() << ": \t" << ratings[3].first() << endl;
	return 0;	
}

运行结果为:

程序很简单, 唯一需要注意的就是两个返回引用的函数的return虽然和返回对象的return一模一样, 但是这两个返回引用的方法不能省略,  至于原因看一下注释

 

 

默认类型模板参数

可以为类型参数提供默认值:

template<class T1, class T2 = int> class Topo {...};

这样, 如果省略T2的值, 编译器将使用int:

// T1和T2都是double
Topo<double, double> m1;

// T1是double, T2是int
Topo<double> m2;

 

模板的具体化

1.隐式实例化

ArrayTP<int, 100> stuff;

编译器在需要对象之前, 不会生成类的隐式实例化:

// 只是声明了一个指针, 没有对象需要创建
ArrayTP<double, 30> * pt;
// 需要创建一个对象
// 编译器生成类定义, 并根据该定义创建一个对象.
pt = new ArrayTP<double, 30>;

2.显示实例化

当使用关键字template并指出所需类型来声明类时, 编译器将生成类声明的显示实例化. 声明必须位于模板定义所在的名称空间中:

// 虽然没有创建和提及类对象, 编译器也将生成类声明(包括方法定义)
template class ArrayTP<string, 100>;

3.显示具体化

显示具体化是特定类型(用于替换模板中的泛型)的定义.有时候可能需要为特殊类型实例化时, 对模板进行修改使其行为不同. 例如, 有一个用于表示排序后数组的类定义了一个模板:

template <typename T>
class SortedArray
{
	...
};

假设上述模板使用>运算符来对值进行比较, name对于数字没有问题, 但是如果T表示的是const char*这种的字符串, 将会失去作用. 在这种情况下, 可以提供一个显示板具体化, 浙江采用为具体类型定义的模板, 而不是为泛型类型定义的模板. 当具体化模板和通用模板都与实例化请求匹配时, 编译器将使用具体化版本:

具体化类模板定义格式如下:

template<> class Classname<specialized-type-name> {...};

使用新的表示法提供一个专供const char*类型使用的SortedArray模板, 可以使用类似下面的代码:

template<> class SortedArray<const char *>
{
	...
};

那么, 当再请求const char*类型的SortedArray模板时, 编译器将使用专用的定义, 而不是通用的模板定义.

// 使用通用的模板定义
SortedArray<int> scores;
// 使用专用的模板定义
SortedArray<const char *> dates;

4.部分具体化

C++还允许部分具体化, 即部分闲置模板的通用性, 例如: 部分具体化可以给类型参数之一指定具体的类型:

// 通用的模板定义
template <class T1, class T2> class Pair {...};

部分具体化的格式如下: 

// 部分具体化
template<class T1> class Pair<T1, int> {...};

关键字template后面的<>声明的是没有被具体化的类型参数. 因此, 上述第二个声明T2具体化为int, 但T1保持不变. 注意如果指定所有的类型, 则<>内将为空, 这将导致显示具体化

template<> class Pair<int, int> {...};

如果有多个模板可供选择, 编译器将使用具体化程度最高的模板, 例如:

// 使用通用的模板定义
Pair<double, double> p1;
// 使用Pair<T1, int>部分具体化
Pair<double, int> p2;
// 使用显示具体化Pair<int, int>
Pair<int, int> p3;

也可以通过为指针提供特殊版本来部分具体化现有的模板:

template<class T>
class Feeb {...};
template<class T *>
class Feeb {...}

如果提供的类型不是指针, 则编译器将使用通用版本, 如果提供的是指针, 编译器将使用指针具体化版本:

// 通用版本
Feeb<char> fb1;
// 指针版本
Feeb<char *> fb2;

如果没有进行部分具体化, 则第二个声明将使用通用模板, 将T转换为char* 类型, 如果进行了部分具体化, 则第二个声明将使用具体化版本, 将T转换为char:

template<class T1, class T2, class T3> class Trio {...};
template<class T1, class T2> class Trio<T1, T2, T2> {...};
template<class T1> class Trio<T1, T1*, T1*> {...};

有下面的代码:

// 调用的是通用模板
Trio<int, short, char *> t1;
// Trio<T1, T2, T2>
Trio<int, short> t2;
// 调用的是Trio<T1, T1*, T1*>
Trio<char, char *, char *> t3;

 

成员模板

就是将模板当作类的成员使用, 来看一个例子:

// tempmemb.cpp
#include <iostream>
using std::cout;
using std::endl;

template <typename T>
class beta
{
private:
	// 模板类中嵌套的模板成员
	// 由于模板类是私有的, 因此只能再beta内使用
	template <typename V>
	class hold
	{
	private:
		V val;
	public:
		hold(V v = 0):val(v){}
		void show() const {cout << val << endl;}
		V Value() const {return val;}
	};
	hold<T> q;
	hold<int> n;
	
public:
	beta(T t, int i):q(t), n(i) {}
	// 模板方法
	template<typename U>
	U blab(U u, T t) {return (n.Value() + q.Value()) * u / t;}
	void Show() const {q.show(); n.show();}	
};

int main()
{
	// 由于T表示double, 因此q的类型为hold<double>
	beta<double> guy(3.5, 3);
	cout << "T was set to double " << endl;
	guy.Show();
	cout << "V was set to T, which is double, then V was set to int" << endl;
	// 将U设置为int类型, 由于blab返回的也是U, 因此虽然里面是double结果, 但是返回会强转成int
	cout << guy.blab(10, 2.5) << endl;
	cout << "U was set to int" << endl;
	// U设置成了double, 因此返回的结果是double类型的
	cout << guy.blab(10.0, 2.5) << endl;
	cout << "U was set to double " << endl;
	cout << "Done" << endl;
	return 0;
}

程序运行结果为: 

程序中需要注意的地方都写有注释

 

将模板用作参数

模板可以包含类型参数(如typename T)和非类型参数(如 int n). 模板还可以包含本身就是模板的参数:

template<template<typename T> class Thing>
class Crab

模板参数是template<typename T>class Thing, 其中template<typename T>class 是类型, Thing是参数. 这意味着, 假设有下面的声明:

Crab<King> legs;

为使上述声明被接受, 模板参数King必须是一个模板类, 其声明与模板参数Thing的声明匹配:

template<typename T>
class King{...};

来看一个完整的例子:

首先是Stack的模板类:

// 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

接着是将模板作为参数使用:

// tempparm.cpp
#include <iostream>
#include "stacktp.h"

template <template<typename T> class Thing>
class Crab
{
private:
	Thing<int> s1;
	Thing<double> s2;
	
public:
	Crab(){};
	bool push(int a, double x) {return s1.push(a) && s2.push(x);}
	bool pop(int & a, double & x) {return s1.pop(a) && s2.pop(x);}
};

int main()
{
	using std::cout;
	using std::endl;
	using std::cin;
	
	// Stack必须匹配template<typename T>
	Crab<Stack> nebula;
	int ni;
	double nb;
	cout << "Enter int double pairs, such as 4 3.5(0 0-to end):" << endl;
	while(cin >> ni >> nb && ni > 0 && nb > 0)
	{
		if(!nebula.push(ni, nb))
			break;
	}
	
	while(nebula.pop(ni, nb))
		cout << ni << ", " << nb << endl;
	
	cout << "Done." << endl;
	return 0;	
}

程序运行结果为:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值