函数模板
创建模板类型T,使得该类型作参数的函数能处理多种类型的数据
template <class T> //定义函数模板,能输出所有类型的数组
void outputArray(const T *array, int count) {
for (int i = 0; i < count; i++)
cout << array[i] << " "; //如果数组元素是类的对象,需要该对象所属类重载了流插入运算符“<<”
cout << endl;
}
类模板
使得类中某些数据成员、函数成员和函数成员返回值能取任意类型
在类外定义成员函数:
template<模板参数表>
类型名 类名 <模板参数标识符列表> ::函数名(参数表)
相当于在类后加<T>,以及在定义前加template <typename T>
#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:
Store();
T &getElem(); //提取数据函数
void putElem(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>::putElem(const T &x) {
// 将haveValue 置为true,表示item中已存入数值
haveValue = true;
item = x; // 将x值存入item
}
int main() {
Store<int> s1, s2;
s1.putElem(3);
s2.putElem(-7);
cout << s1.getElem() << " " << s2.getElem() << endl;
Student g = { 1000, 23 };
Store<Student> s3;
s3.putElem(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;
}
数组类模板
对象存在赋值=运算的时候一般都需要重载复制构造函数
通过[]希望能改变数组值,所以重载[]时返回一个引用,以及一个不用于改变数组值的常引用
//因为Array对象中含有指针成员,所以复制构造函数中new新分配内存,执行深层复制
template <class T>
Array<T>::Array(const Array<T> &a) { //复制构造函数
size = a.size; //从对象x取得数组大小,并赋值给当前对象的成员
list = new T[size]; // 动态分配n个T类型的元素空间
for (int i = 0; i < size; i++) //从对象X复制数组元素到本对象
list[i] = a.list[i];
}
//重载=号,因为希望=号的返回值为左值,即=号可以改变对象
//所以重载类型为Array<T> & 的引用
template <class T>
Array<T> &Array<T>::operator = (const Array<T>& rhs) {
if (&rhs != this) {
//如果本对象中数组大小与rhs不同,则删除数组原有内存,然后重新分配
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; //返回当前对象的引用
}
//重载指针转换运算符,将Array类的对象名转换为T*类型的指针
template <class T>
Array<T>::operator T * () {
return list; //返回当前对象中私有数组的首地址
}
//重载指针转换运算符的应用
void read(int *p, int n) {
for (int i = 0; i < n; i++)
cin >> p[i];
}
Array<int> a(10);
read(a, 10);//调用read时会用int代替T,所以重载T*即改变指针为int *
链表与结点类模板
链表由一系列结点组成,每个节点包括
1)数据域
2)指向下一结点的指针
对结点进行操作时应先保证后方结点的稳定
1.插入新结点
在结点后插入一个结点时,因为结点是由前方的结点决定的,所以要先连接新结点与后方的结点,再连接当前结点与新结点
template <class T>
void Node<T>::insertAfter(Node<T> *p) {
//p节点指针域指向当前节点的后继节点
p->next = next; // p->next表示中间插入的新结点,next中的内容为之前d1指向的d2结点
next = p; //next表示当前节点的后继指针,该指针应指向新结点p
}
2.删除结点后的结点
Node<T> *Node<T>::deleteAfter(void) {
Node<T> *tempPtr = next;//先保存要删除结点的后续结点,next是要删除结点的前一个结点,即当前结点的后续指针
if (next == 0) //表示当前结点为尾结点
return 0;
next = tempPtr->next;
return tempPtr;
}
栈
栈的应用举例:表达式处理
入栈:
void Stack<T, SIZE>::push(const T &item) {
assert(!isFull());
list[++top] = item;
}
出栈:
T Stack<T, SIZE>::pop() {
assert(!isEmpty());
return list[top--];
}
清空栈
template <class T, int SIZE>
void Stack<T, SIZE>::clear() {
top = -1;
}