引例整数和浮点数求绝对值
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;
}
函数模版定义语法
- 语法形式
- 模版参数表的内容
- 类型参数: 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(); //模版实参列表<>为空,默认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