# 引例整数和浮点数求绝对值

  • 使用两种重载函数?
int abs(int x)
{
	return x<0? -x:x;
}
double abs(double x)
{
	return x<0? -x:x;
}
  • 解决:函数模版
    • 创建一个通用功能的函数
    • 支持多种不同形参
    • 简化重载函数的函数体设计
/*
* project : 模版求绝对值
*/
#include <iostream>
using namespace std;
template<typename T>  //T 也可使用其它名称
T abs(T x)
{
	return x < 0 ? -x : x;
}
int main(void)
{
	int n = -5;
	double d = -5.5;
	cout << abs(n) << endl;
	cout << abs(d) << endl;
	return 0;
}

# 函数模版定义语法

  • 语法形式
    • template <模版参数表>
    • 函数定义
  • 模版参数表的内容
    • 类型参数: class (或 typename) 标识符
    • 常量参数:类型说明符 标识符
    • 模版参数: template <参数表> class 标识符

# 函数模版

#include <iostream>
using namespace std;
template <class T> // 定义函数模版
void outputArray(const T* array, int count)
{
	for (int i = 0; i < count; i++)
	{
		cout << array[i] << " "; // 如果数组元素是类的对象,需要该对象所属类重载流插入运算符 "<<"
	}
	cout << endl;
}
int main(void)
{
	const int A_count = 8, B_count = 8, C_count = 20;
	int a[A_count] = { 1,2,3,4,5,6,7,8 };
	double b[B_count] = { 1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8 };
	char c[C_count] = "welcome";
	cout << "a array contains:" << endl;
	outputArray(a, A_count);
	cout << "b array contains:" << endl;
	outputArray(b, B_count);
	cout << "c array contains:" << endl;
	outputArray(c, C_count);
	return 0;
}
/*
a array contains:
1 2 3 4 5 6 7 8
b array contains:
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8
c array contains:
w e l c o m e
*/
  • 注意:
    • 一个函数模版并非自动可以处理所有类型的数据
    • 只有能够进行函数模版中运算的类型,可以作为类型实参
    • 自定义的类,需要重载模版中的运算符,才能作为类型实参

# 类模版的作用

  • 使用类模版使用户可以为类声明一种模式,使得类中的某些数据成员,某些成员函数的参数,某些成员函数的返回值,能去 “任意” 类型 (包含基本类型的和用户自定义类型)

# 类模版的声明

template <模版参数表>
class 模版
{
	模版声明
}
  • 如果需要在类模版以外定义其成员函数,则要采用以下形式:
    • template <模版参数表>
    • 类型名 类名 <模版参数标识符列表>:: 函数名 (参数表)
  • 模版参数表中参数可以声明为该模版类的友元类
  • 可通过 typedef 或者 using 对实例化的类模版定义别名

# 模版的默认实参

  • 类似于函数形参可有默认实参,函数 / 类模版可有默认模版实参
template <typename T = double>
class Point
{
public:
	Point(T _x = 0,T _y = 0):x(_x),y(_y)
	{
	
	}
private:
	T x;
	T y;
}

# 使用模版

Point<int> Point(); // 给定模版参数 int,定义整数点对象 (0,0)
Point<> Point(); // 模版实参列表 & lt;> 为空,默认 double 类型初始化

# 类模版实例

#include <iostream>
#include <cstdlib>
using namespace std;
struct Student
{
	int id; // 学号
	float gpa; // 平均分
};
template <class T>
class Store // 类模版,实现对任意数据进行存取
{
private:
	T item;  //item 用于存放任意类型的数据
	bool haveValue; //haveValue 标记 item 是否已被存入内容
public:
	friend T; // 将参数类型 T 声明为友元类
	Store();
	T& getElem(); // 提取数据函数
	void pubElem(const T& x); // 存入数据函数
};
template <class T>
Store<T>::Store() :haveValue(false)
{
}
template <class T>
T& Store<T>::getElem()
{
	// 如试图提取未初始化的数据,则终止程序
	if (!haveValue)
	{
		cout << "No item present!" << endl;
		exit(1); // 使程序完全退出,返回到操作系统
	}
	return item; // 返回 item 中存放的数据
}
template <class T>
void Store<T>::pubElem(const T& x)
{
	//haveValue 置为 true,标识 item 中已存入数值
	haveValue = true;
	item = x; // 将 x 值存入 item
}
int main(void)
{
	using IntStore = Store<int>;
	IntStore s1, s2;
	s1.pubElem(3);
	s2.pubElem(-7);
	cout << s1.getElem() << " " << s2.getElem() << endl;
	Student g = { 1000,23 };
	Store<Student> s3;  // 将结构体传给类 T 类型换成 Student 结构体
	s3.pubElem(g);
	cout << "The student id is " << s3.getElem().id << endl;
	Store<double> d;
	cout << "Retrieving object D...";
	cout << d.getElem() << endl;
	//d 未初始化,执行函数 D.getElement () 时导致程序终止
	return 0;
}
/*
3 -7
The student id is 1000
Retrieving object D...No item present!
*/

# 群体的概念

  • 群体时指由多个数据元素组成的集合体,群体可以分为两个大类:线性群体和非线性群体。
  • 线性群体中的元素按位置排列有序,可以区分为第一个元素,第二个元素
  • 非线性群体不用位置顺序来标识元素
  • vector 就是用类模版实现的动态数组

# 动态数组类模版程序:返回 0~N 的质数

#pragma once
/*
动态类数组头指针 Array.h
*/
#ifndef ARRAY_H
#define ARRAY_H
#include <cassert>
template <class T> // 数组类模版定义
class Array
{
private:
	T* list;	// 用于存放动态分配的数组内存首地址
	int size; // 数组大小 (元素个数)
public:
	Array(int sz = 50); // 构造函数
	Array(const Array<T>& a); // 复制构造函数
	~Array();   // 析构函数
	Array<T>& operator = (const Array<T> &rhs);  // 重载 "="
	T& operator[](int i);  // 重载 []
	const T& operator [](int i) const;// 重载 "[]" 常函数
	operator T* ();   // 重载到 T * 类型的转换
	operator const T* () const;
	int getSize() const;  // 取数组的大小
	void resize(int sz); // 修改数组的大小
};
template <class T>Array<T>::Array(int sz) // 构造函数
{
	assert(sz >= 0);//sz 为数组的大小,应当非负
	size = sz;    // 将元素个数赋值给 size
	list = new T[size]; // 动态分配 size 个 T 类型的元素空间
}
template <class T>Array<T>::~Array() // 析构函数
{
	delete[] list;
}
template <class T>
Array<T>::Array(const Array<T>& a)  // 复制构造函数
{
	size = a.size;
	list = new T(size); // 动态分配 n 个 T 类型的元素空间
	for (int i = 0; i < size; i++)
	{
		list[i] = a.list[i];
	}
}
// 重载 “=” 运算符,将对象 rhs 赋值给本对象,实现对象之间的整体赋值
template <class T>
Array<T>& Array<T>::operator=(const Array<T>& rhs)
{
	// 如果本对象中数组大小与 rhs 不同,则删除数组原有内存,然后重新分配
	if (&rhs != this)
	{
		if (size != rhs.size)
		{
			delete[] list; // 删除数组原有内存
			size = rhs.size;	// 设置本对象的数组大小
			list = new T[size]; // 重新分配 size 个元素的内存
		}
		// 从对象 x 复制数组元素到本对象
		for (int i = 0; i < size; i++)
		{
			list[i] = rhs.list[i];
		}
	}
	return *this;
}
// 重载下标运算符,实现与普通数组一样通过下标访问元素,具有越界检查功能
template <class T>
T& Array<T>::operator[](int n)
{
	assert(n >= 0 && n < size); // 检查下标是否越界
	return list[n];  // 返回下标为 n 的数组元素
}
template <class T>
const T & Array<T>::operator[] (int n) const
{
	assert(n >= 0 && n < size);// 检查下标是否越界
	return list[n];  // 返回下标为 n 的数组元素
}
// 重载指针转换运算符,将 Array 类的对象名转换为 T 类型的指针
template <class T>
Array<T>::operator T* ()
{
	return list;  // 返回当前对象中私有数组的首地址
}
// 取当前数组大小
template<class T>
int Array<T>::getSize() const  // 返回数组大小
{
	return size;
}
// 将数组大小修改为 sz
template <class T>
void Array<T>::resize(int sz)   // 如果哦原有复制空间过下,重新分配数组内存
{
	assert(sz >= 0);// 检查 sz 是否非负
	if (sz == size) // 如果指定的大小与原有大小一样,什么也不做
		return;
	T* newList = new T[sz];  // 申请新的数组内存
	int n = (sz < size) ? sz : size;// 将 sz 与 size 中较小的一个赋值给 n
	// 将原有数组中前 n 个元素复制到新数组中
	for (int i = 0; i < n; i++)
	{
		newList[i] = list[i];
	}
	delete[] list;	// 删除原数组
	list = newList;  // 使 list 指向新数组
	size = sz;
}
#endif // !ARRAY_H

/* 求 2~N 中的质数 main.h 主函数 */
#include "Array.h"
#include <iostream>
#include <iomanip>
using namespace std;
void read(int* p, int n)
{
	for (int i = 0; i < n; i++)
	{
		cin >> p[i];
	}
}
int main(void)
{
	// 用来存放质数的数组,初始状态有 10 个元素
	Array<int>a(3);
	//read(a, 10);
	int n, count = 0;
	cout << "Enter a value > = 2 as upper limit for prime number:";
	cin >> n;
	for (int i = 2; i <= n; i++)
	{
		bool isPrime = true;  // 默认 i 是质数
		for (int j = 0; j < count; j++)  // 判断是否为质数
		{
			if (i % a[j] == 0) // 如果
			{
				isPrime = false;
				break;
			}
		}
		if (isPrime)
		{
			if (count == a.getSize())
				a.resize(count * 2);
			a[count++] = i;
		}
	}
	for (int i = 0; i < count; i++)
	{
		cout << setw(8) << a[i];
	}
	cout << endl;
	return 0;
}

# 链表类模版

#pragma once
#ifndef NODE_H
#define NODE_H
// 单链表的结点类模版 NOde.h
template <class T>
class Node
{
private:
	Node<T>* next;  // 指向后继节点的指针
public:
	T data;  // 数据域
	Node(const T& item, Node<T>* next = 0);  // 构造函数
	void insertAfter(Node<T>* p); // 在本结点之后插入一个同类结点 P
	Node<T>* deleteAfter();  // 在删除本结点的后继节点,并返回其地址
	Node<T>* nextNode();  // 获取后继节点的地址
	const Node<T>* nextNode() const;  // 获取后续节点的地址
};
// 类的实现函数
// 构造函数,初始化数据和指针成员
template <class T>
Node<T>::Node(const T& data, Node<T>* next = 0)
	:data(data), next(next)
{
}
// 返回后继节点的指针
template <class T>
Node<T>* Node<T>::nextNode()
{
	return next;
}
// 返回后继节点的指针
template <class T>
const Node<T>* Node<T>::nextNode() const  // 返回的常指针,只能读
{
	return next;
}
// 在结点之后插入一个结点
template <class T>
void Node<T>::insertAfter(Node<T>* p)
{
	//p 结点指针域指向当前节点的后继节点
	p->data = next;
	next = p;  // 当前节点的指针域指向 p
}
// 删除节点之后的结点
template <class T>
Node<T>* Node<T>::deleteAfter(void)
{    // 结点物理上不直接删除
	Node<T>* tempPtr = next;
	if (next == 0)   // 代表为空指针
		return 0;
	next = tempPtr->next; //
	return tempPtr;  // 返回的就是被删除的指针
}
#endif // !NODE_H