此份笔记建议在完整阅读郑莉老师、董渊老师、何江舟老师所编写的《C++语言程序设计(第4版)》后食用,风味更佳!
最后,由于本人水平有限,笔记中仍存在错误但还没有被检查出来的地方,欢迎大家批评与指正。
第9章 群里类和群体数据的组织
9.1 函数模板与类模板
9.1.1 函数模板
1.函数模板的定义
template<typename 标识符>
类型名 函数名(参数表)
{
函数体
}
- typename可以替换成class
2.实例
//求绝对值的函数模板及其应用
#include<iostream>
using namespace std;
template<class T>
T abs(T x) {
return x < 0 ? -x : x;
}
int main()
{
int n = -5;
double d = -5.5;
cout << abs(n) << endl;
cout << abs(d) << endl;
while (1);
}
3.模板函数的实例化
-
当类型参数的含义确定后,编译器将以函数模板为样板,生成一个函数,这一过程称为模板函数的实例化。
-
上述模板函数的实例
-
int abs(int x){ return x < 0 ? -x : x; }
-
9.1.2 类模板
1.类模板声明的语法
template<模板参数表>
class 类名
{
类成员声明
};
2.在类模板以外定义其成员函数
template<class 模板参数标识符列表>
返回值类型名 类名<模板参数标识符列表>::函数名(参数表)
3.使用一个模板类来建立对象
类名<指定的数据类型>对象名1,对象名2,...
4.实例1
#include <iostream>
#include <cstdlib>
using namespace std;
// 结构体Student
struct Student
{
int id; //学号
float gpa; //平均分
};
template <class T>
//类模板:实现对任意类型数据进行存取
class Store
{ private:
T item; // 用于存放任意类型的数据
int haveValue; // 用于标记item是否已被存入内容
public:
Store(void); // 默认形式(无形参)的构造函数
T GetElem(void); //提取数据函数
void PutElem(T x); //存入数据函数
};
// 默认形式构造函数的实现
template <class T>
Store<T>::Store(void): haveValue(0) {}
template <class T> // 提取数据函数的实现
T Store<T>::GetElem(void)
{ // 如果试图提取未初始化的数据,则终止程序
if (haveValue == 0)
{ cout << "No item present!" << endl;
exit(1);
}
return item; // 返回item中存放的数据
}
template <class T> // 存入数据函数的实现
void Store<T>::PutElem(T x)
{ haveValue++; // 将haveValue 置为 TRUE,表示item中已存入数值
item = x; // 将x值存入item
}
void main(void)
{ Student g= {1000, 23};
Store<int> S1, S2;
Store<Student> S3;
Store<double> D;
S1.PutElem(3);
S2.PutElem(-7);
cout << S1.GetElem() << " " << S2.GetElem() << endl;
S3.PutElem(g);
cout << "The student id is " << S3.GetElem().id << endl;
cout << "Retrieving object D " ;
cout << D.GetElem() << endl; //输出对象D的数据成员
// 由于D未经初始化,在执行函数D.GetElement()时出错
}
9.1.3 讨论
1.模板实参的使用
- 函数模板的声明中有两个形参表:一个是模板形参表,一个是函数形参表。在模板函数的调用格式中相应地也有两个实参表:模板实参表和函数实参表。但模板实参在一定条件下可省略,若所有的模板实参都省略,则整个模板实参表也可以省略。
- 模板实参必需同时满足两个条件才可省略:
- 该模板实参所提供的信息同样可以通过函数实参提供的
- 所有这样的模板实参集中位于参数表的最后
实例:
#include<iostream>
using namespace std;
template <typename T>
T min1(T a, T b)
{ return a<b? a:b; }
template <typename T1, typename T2>
T1 min2(T1 a, T2 b)
{ return a<b? a:b; }
int main()
{
cout<<showpoint<<min1(3.0, 5.0)<<endl;
cout<<showpoint<<min1<int>(3, 5.0)<<endl;
cout<<showpoint<<min1<double>(3, 5.0)<<endl;
cout<<showpoint<<min2(3, 5.0)<<endl;
return 0;
}
//运行结果
3.000000
3
3.000000
3
2.模板的常规参数
- 函数模板不但可以有用typename(或class)声明的虚拟类型参数,也可以有用一般类型修饰符(如int、double等)声明的常规参数。常规参数所对应的实参只能是一个常量表达式。由于无法借助于函数实参来提供相同的信息(像虚拟类型参数那样),常规参数所对应的实参不得省略。下面通过一个例子说明如何在函数模板中使用常规参数。
实例:
模板函数sumRow对一个二维数组按行求和,结果存入一个一维数组的对应单元中。作为数据源的二维数组以及存放结果的一维数组作为函数的参数给出,数组类型以及二维数组的行数和列数作为模板的参数给出。
//sumRow.h
#include<iostream>
using namespace std;
//下面的Rows、Cols表示行数和列数
template<typename Type,int Rows,int Cols>
void sumRow(Type data[][Cols],Type result[]){
for(int i=0;i<Rows;i++){
result[i]=0;
for(int j=0;j<Cols;j++) result[i]+=data[i][j];
}
}
//3.cpp
#include"sumRow.h"
int main(){
int d[][3]={{1,2,3},{2,3,4},{4,5,6},{6,7,8}};
int r[4];
sumRow<int,4,3>(d,r); //必须显式给出对应于常规参数的模板实参
cout<<r[0]<<' '<<r[1]<<' '<<r[2]<<' '<<r[3]<<endl;
double dd[][4]={{1.1,2.2,3.3,4.4},{2.2,3.3,4.4,5.5},{3.3,4.4,5.5,6.6},
{4.4,5.5,6.6,7.7},{5.5,6.6,7.7,8.8}};
double rr[5];
sumRow<double,5,4>(dd,rr);//必须显式给出对应于常规参数的模板实参
cout<<rr[0]<<' '<<rr[1]<<' '<<rr[2]<<' '<<rr[3]<<' '<<rr[4]<<endl;
return 0;
}
//运行结果
6 9 15 21
11 15.4 19.8 24.2 28.6
3.实例
题目:设计一个类模板MyArray,用来实现可以任意指定下标范围的任意类型的数组
//MyArray.h
#include<iostream>
using namespace std;
template<typename T>
class MyArray{
int min_index;
int max_index;
T *data;
public:
MyArray( int min, int max);
MyArray( int max);
~MyArray( ){ delete []data; };
T & operator[](int index)const;
int getMinIndex()const{ return min_index; }
int getMaxIndex()const{ return max_index; }
};
template<typename T>
MyArray<T>::MyArray( int min, int max): min_index(min),max_index(max){
if(min>max){ //若下标范围的下界大于上界,显示出错信息并退出
cout<<“Error index range”;
exit(1);
}
data=new T[max_index-min_index+1];
}
template<typename T>
MyArray<T>::MyArray( int max): min_index(0),max_index(max){
data=new T[max_index+1];
}
template<typename T>
T & MyArray<T>::operator [](int index)const{
if(index<min_index || index>max_index){
//若下标越界,显示出错信息并退出
cout<<"Index out of range";
exit(1);
}
return data[index-min_index];
}
template<typename T>
istream &operator>>(istream &is, MyArray<T> &a){
for(int i=a.getMinIndex(); i<=a.getMaxIndex(); i++) is>>a[i];
return is;
}
template<typename T>
ostream &operator <<(ostream &os, const MyArray<T> &a){
for(int i=a.getMinIndex(); i<=a.getMaxIndex(); i++) os<<a[i]<<' ';
return os;
}
//14_4.cpp
#include"MyArray.h"
int main(){
MyArray<int> x(3,8);
cout<<“请输入”<<(x.getMaxIndex()-x.getMinIndex()+1)<<“个数据:”;
cin>>x;
cout<<“刚输入的数据是:”;
cout<<x<<endl;
return 0;
}
9.1.4 类模板与继承
1.类模板继承普通类
class Base{
int data;
public:
Base(int n):data(n){}
int get_data()const{
return data;
}
};
Template<typename T>
Class tempDerived: public Base
T value;
public:
tempDerived(int n, T m):Base(n),value(m){}
T sum() const{
return value+(T)get_data();
}
};
2.类模板继承模板类
Template<typename T>
class Base{
T data;
public:
Base(T n):data(n){}
T get_data()const{
return data;
}
};
Template<typename T1>
Class tempDerived: public Base<int>
T1 value;
public:
tempDerived(int n, T1 m):Base<int>(n),value(m){}
T1 sum() const{
return value+(T1)get_data();
}
};
3.类模板继承类模板
Template<typename T>
class Base{
T data;
public:
Base(T n):data(n){}
T get_data()const{
return data;
}
};
Template<typename T1, typename T2 >
Class tempDerived: public Base<T1>
T2 value;
public:
tempDerived(T1 n, T2 m):Base<T1>(n),value(m){}
T2 sum() const{
return value+(T2)get_data();
}
};
4.普通类继承模板类
Template<typename T>
class Base{
T data;
Public:
Base(T n):data(n){}
T get_data()const{ return data;}
};
Class tempDerived: public Base<int>
double value;
public:
tempDerived(int n, double m):Base<int>(n),value(m){}
double sum() const{
return value+(double)get_data();
}
};
5.类模板继承模板参数给出的基类
题目:设计模板类Derived,使得在创建一个Derived对象时可以动态确定它的基类
class Base_A{
public:
Base_A(){
cout<<"创建Base_A"<<endl;
}
};
class Base_B{
public:
Base_B(){
cout<<"创建Base_B"<<endl;
}
};
template<class T>
class Derived:public T{
public:
Derived():T(){
cout<<"创建Derived"<<endl;
}
};
int main(){
Derived<Base_A> a;
Derived<Base_B> b;
return 0;
}
//DynaBaseTest.cpp
#include"DynaBase.h"
int main(){
Derived<Base_A> a;
Derived<Base_B> b;
return 0;
}