C++类模板用法详解(附带实例)
C++ 中,关键字 template 不但可以定义函数模板,还可以定义类模板。
我们定义过很多类,每个类的成员变量及成员函数的参数、返回值类型都是确定的。类模板的作用则是,类中的成员变量及成员函数的参数、返回值类型不具体指定,编译时自动根据传入的数据类型进行判断。
本质上,类模板是用类生成类,能有效减少类的定义数量。
类模板中成员函数的定义形式如下:
同样,类模板不是一个真实的类,传入实参后才会生成真实的类,即模板类。模板类与普通类相同,是类模板中的类型参数实例化后得到的真实类。
使用模板类定义对象的形式如下:
例如,定义一个表示容器的类模板 Container,程序代码如下:
和普通类一样,需要对类模板成员函数进行实现:
将模板类的参数设置为整型,然后用得到的模板类声明一个对象,程序代码如下:
声明 myContainer 对象后,就可以调用相应的类成员函数了,程序代码如下:
定义类模板时,“类型形式参数表”中的参数还可以是其他类模板。例如:
类模板可以继承。例如,下面的代码中 T 是一个类,类模板 CDerived 继承自类 T:
【实例】定义一个简单的类模板,在主函数中调用该类模板,输出成员变量的值。代码如下:
【实例】定义一个带默认类型参数的类模板,然后在主函数中调用它,输出数据。代码如下:
【实例】类型默认值。为具体类型的参数提供默认值,具体代码如下:
要想获取下标值,需要重载数组下标运算符“[]”,然后实例化数组模板类,进行下标越界检测。程序代码如下:
assert() 函数需要使用 cassert 头文件,要使用 #include 命令包含。程序能够及时发现 dateArray 数组发生了越界,因为定义数组时指定其长度为 3,当数组下标为3时说明数组中有 4 个元素,所以程序执行到 dateArray[3] 时,将弹出错误警告。
【实例】定制一个类模板,覆盖类模板中定义的 Month、Day、Year 成员,具体代码如下:
【实例】定制一个类模板,覆盖其成员函数 Display(),代码如下:
我们定义过很多类,每个类的成员变量及成员函数的参数、返回值类型都是确定的。类模板的作用则是,类中的成员变量及成员函数的参数、返回值类型不具体指定,编译时自动根据传入的数据类型进行判断。
本质上,类模板是用类生成类,能有效减少类的定义数量。
C++类模板的定义与声明
类模板的定义形式如下:template <类型形式参数表> class 类模板名 { 类模板体 };
类模板中成员函数的定义形式如下:
template <类型形式参数表> 返回类型 类模板名 <类型名表>::成员函数名(形式参数列表) { 函数体 }其中,关键字 template 表示定义的是一个模板;尖括号内为该模板的类型形式参数表,不能为空,多个类型参数间用逗号隔开。
同样,类模板不是一个真实的类,传入实参后才会生成真实的类,即模板类。模板类与普通类相同,是类模板中的类型参数实例化后得到的真实类。
使用模板类定义对象的形式如下:
类模板名<类型实际参数表> 对象名
例如,定义一个表示容器的类模板 Container,程序代码如下:
template <class Type> //模板头,Type 表示类中成员变量和成员函数参数的类型 class Container { Type tItem; //成员变量 tItem public: Container(); //构造函数 void begin(const Type& tNew); //声明成员函数 begin() void end(const Type& tNew); //声明成员函数 end() void insert(const Type& tNew); //声明成员函数 insert() void empty(const Type& tNew); //声明成员函数 empty() };
和普通类一样,需要对类模板成员函数进行实现:
void Container<Type>:: begin (const Type& tNew) //定义成员函数 begin(),查找容器的第一个元素 { tItem=tNew; } void Container<Type>:: end (const Type& tNew) //定义成员函数 end(),查找容器的最后一个元素 { tItem=tNew; } void Container<Type>:: insert(const Type& tNew) //定义成员函数 insert(),向容器中插入元素 { tItem=tNew; } void Container<Type>:: empty(const Type& tNew) //定义成员函数 empty(),清空容器 { tItem=tNew; }
将模板类的参数设置为整型,然后用得到的模板类声明一个对象,程序代码如下:
Container<int> myContainer; //声明 Container<int>类对象
声明 myContainer 对象后,就可以调用相应的类成员函数了,程序代码如下:
int i=10; myContainer.insert(i);
定义类模板时,“类型形式参数表”中的参数还可以是其他类模板。例如:
template <template <class A> class B> //模板头,B 表示成员变量 m_n 的类型,仍然是一个类模板 class CBase { private: B<int> m_n; //成员变量 m_n };
类模板可以继承。例如,下面的代码中 T 是一个类,类模板 CDerived 继承自类 T:
template <class T> //模板头,T 表示本类继承的基类。注意,类也是一种数据类型 class CDerived public T { public: CDerived(); //构造函数 CDerived() }; template <class T> //模板头,T 表示本类继承的基类 CDerived<T>:: CDerived() : T() { cout << "" << endl; } int main() //主函数 { CDerived<CBase1> D1; //T 为 CBase1 类,使用实例化后的 CDerived 类定义对象 D1 }注意,为类模板定义成员函数时,类模板名之后要添加“<类型名表>”,其他和普通成员函数的定义完全一样。在类外定义成员函数时尤其要注意,不要漏掉。
C++简单类模板
类模板中的类型形式参数表,可以在执行时指定,也可以在定义类模板时指定。下面介绍如何在执行时指定类型参数。【实例】定义一个简单的类模板,在主函数中调用该类模板,输出成员变量的值。代码如下:
#include <iostream> using namespace std; template <class T1,class T2> //模板头,T1、T2 为待显示两个数的数据类型 class MyTemplate //定义类模板 MyTemplate { T1 t1; T2 t2; public: MyTemplate(T1 tt1,T2 tt2) //构造函数 MyTemplate() { t1=tt1; t2=tt2; } void display() //成员函数 display() { cout << t1 << ' ' << t2 << endl; } }; int main() //主函数 { int a=123; double b=3.1415; MyTemplate<int,double> mt1(a,b); //实例化类模板 MyTemplate(),定义对象 mt1 mt1.display(); //调用 mt1 的成员函数 display() }程序运行结果为:
123 3.1415其中,MyTemplate 是一个类模板,它使用整型和双精度型作为参数。
C++默认模板参数
默认模板参数是指在类模板定义时,可以为“类型形式参数表”中的某个类型参数设置默认值。有默认值的类型参数,在实例化类模板时可以不指定数据类型,使用默认类型。但如果指定了,则使用指定类型。【实例】定义一个带默认类型参数的类模板,然后在主函数中调用它,输出数据。代码如下:
#include <iostream> using namespace std; template <class T1,class T2 = int> //模板头,T1、T2 为待显示两个数的数据类型,T2 默认为 int 型 class MyTemplate //定义类模板 MyTemplate { T1 t1; T2 t2; public: MyTemplate(T1 tt1,T2 tt2) //构造函数 MyTemplate() { t1=tt1; t2=tt2; } void display() //成员函数 display() { cout << t1 << '' << t2 << endl; } }; int main() { int a=123; double b=3.1415; MyTemplate<int,double> mt1(a,b); //实例化类模板,定义对象 mt1,其中第 2 个参数指定为 double 型 MyTemplate<int> mt2(a,b); //实例化类模板,定义对象 mt2,其中第 2 个参数使用默认的 int 型 mt1.display(); //调用 mt1 的成员函数 display() mt2.display(); //调用 mt2 的成员函数 display() }程序运行结果为:
123 3.1415 123 3
C++为具体类型的参数提供默认值
类模板定义时,还可以在“类型形式参数表”中声明一个确定类型的变量并为其赋默认值。实例化类模板时,如果参数中含有该变量,则使用其默认值。【实例】类型默认值。为具体类型的参数提供默认值,具体代码如下:
#include <iostream> using namespace std; template <class T1,class T2,int num= 10 >//模板头,T1、T2 为两个数的数据类型,声明 int 型变量 num 并赋值 10 class MyTemplate//定义类模板 MyTemplate { T1 t1; T2 t2; public: MyTemplate(T1 tt1,T2 tt2) //构造函数 MyTemplate() { t1=tt1+num; t2=t2-num; } void display() //成员函数 display() { cout << t1 << '' << t2 <<endl; } }; int main() { int a=123; double b=3.1415; MyTemplate<int,double> mt1(a,b); //实例化类模板,定义对象 mt1,不声明默认参数 num MyTemplate<int,double,100> mt2(a,b); //实例化类模板,定义对象 mt2,声明默认参数 num mt1.display(); //调用 mt1 的成员函数 display() mt2.display(); //调用 mt2 的成员函数 display() }程序运行结果为:
133 13.1415 223 103.141
C++有界数组模板
C++ 中,数组下标越界会造成程序崩溃,然而程序员往往很难发现此类错误。能否让数组自行检测下标是否越界呢?当然可以,方法是建立一个数组类模板,对数组下标进行检查。要想获取下标值,需要重载数组下标运算符“[]”,然后实例化数组模板类,进行下标越界检测。程序代码如下:
#include <iostream> #include <iomanip> #include <cassert> using namespace std; class Date //定义 Date 类 { int iMonth,iDay,iYear; char Format[12]; public: Date(int m=0,int d=0,int y=0) //构造函数 Date() { iMonth=m; iDay=d; iYear=y; } friend ostream& operator<<(ostream& os,const Date t) //重载 "<<" 运算符 { cout <<"Month:"<< t.iMonth <<' '; cout <<"Day:"<< t.iDay<<' '; cout <<"Year:"<< t.iYear<<' '; return os; } void Display() //成员函数 Display(),输出 Month、Day、Year { cout <<"Month:"<< iMonth; cout <<"Day:"<< iDay; cout <<"Year:"<< iYear; cout <<endl; } }; template <class T,int b> //模板头,T 表示数组类型,b 表示数组元素个数 class Array //定义数组类模板 Array { T elem[b]; public: Array() {} //成员函数 Array() T& operator[] (int sub) //重载数组下标运算符 "[]" { assert(sub>=0 && sub<b); //如果数组下标越界,则弹出警告 return elem[sub]; } }; int main() //主函数 { Array<Date,3> dateArray; //实例化数组类模板 Array,得到数组 dateArray,包含 3 个元素 Date d1(1,2,3); //实例化 Date 类对象 d1 Date d2(4,5,6); //实例化 Date 类对象 d2 Date d3(7,8,9); //实例化 Date 类对象 d3 dateArray[0]=d1; //以下 3 行为数组 dateArray 赋值 dateArray[1]=d2; dateArray[2]=d3; for(int i=0;i<3;i++) //循环输出 dateArray 的 3 个元素值 cout << dateArray[i] << endl; Date d4(10,11,13); //定义 Date 类,得到对象 d4 dateArray[3]=d4; //为 dateArray[3]赋值,发生下标越界 cout << dateArray[3] << endl; //输出 dateArray[3],发生下标越界 }程序运行结果为:
Month:1 Day:2 Year:3 Month:4 Day:5 Year:6 Month:7 Day:8 Year:9 Assertion failed! Program: C:\Users\xiexuewu\Desktop\1.exe File: C:\Users\xiexuewu\Desktop\未命名1.cpp, Line 41 Expression: sub>=0 && sub<b程序中使用 assert() 函数进行警告处理,当有下标越界情况发生时,弹出对话框警告,然后输出出现错误的代码位置。
assert() 函数需要使用 cassert 头文件,要使用 #include 命令包含。程序能够及时发现 dateArray 数组发生了越界,因为定义数组时指定其长度为 3,当数组下标为3时说明数组中有 4 个元素,所以程序执行到 dateArray[3] 时,将弹出错误警告。
C++类模板的定制
根据类模板得到一个模板类后,可以扩展类的功能,对类模板进行覆盖,以完成特殊的功能。覆盖操作可以针对整个类模板、部分类模板以及类模板中的成员函数,这种覆盖操作称为定制。【实例】定制一个类模板,覆盖类模板中定义的 Month、Day、Year 成员,具体代码如下:
#include <iostream> using namespace std; class Date //定义 Date 类 { int iMonth,iDay,iYear; char Format[128]; public: Date(int m=0,int d=0,int y=0) //构造函数 Date() { iMonth=m; iDay=d; iYear=y; } friend ostream& operator<<(ostream& os,const Date t) //重载运算符 "<<" { cout <<"Month:"<< t.iMonth <<' '; cout <<"Day:"<< t.iDay<<' '; cout <<"Year:"<< t.iYear<<' '; return os; } void Display() //成员函数 Display() { cout <<"Month:"<< iMonth; cout <<"Day:"<< iDay; cout <<"Year:"<< iYear; cout <<endl; } }; template <class T> class Set //定义类模板 Set { T t; public: Set(T st) : t(st) {} //构造函数 void Display() { cout << t << endl; } }; template <> class Set<Date> //定制模板类 Set,参数为 Date 对象,相当于实例化模板类 { Date t; public: Set(Date st) : t(st) {} //构造函数 void Display() //Display()函数输出的是 Date 对象 { cout <<"Date :"<< t << endl; } }; int main() //主函数 { Set<int> instSet(123); //实例化 Set<int> Set<Date> dt =Date(1,2,3); //实例化 Set<Date> instSet.Display(); //调用 Display() dt.Display(); //调用 Display() }程序运行结果为:
123 Date :Month:1 Day:2 Year:3程序中使用类 Date 定制整个类模板,即模板类构造函数中的参数是 Date 对象,成员函数 Display() 输出的也是 Date 对象。定制类模板相当于实例化一个模板类。
【实例】定制一个类模板,覆盖其成员函数 Display(),代码如下:
#include <iostream> using namespace std; class Date //定义 Date 类 { int iMonth,iDay,iYear; char Format[128]; public: Date(int m=0,int d=0,int y=0) //构造函数 { iMonth=m; iDay=d; iYear=y; } friend ostream& operator<<(ostream& os,const Date t) //重载运算符 { cout << "Month:" << t.iMonth << ' '; cout << "Day:" << t.iDay << ' '; cout << "Year:" << t.iYear << ' '; return os; } void Display() //成员函数 Display() { cout << "Month:" << iMonth; cout << "Day:" << iDay; cout << "Year:" << iYear; cout << std::endl; } }; template <class T> class Set //定义类模板 Set { T t; public: Set(T st) : t(st) {} //构造函数 void Display(); }; template <class T> void Set<T>::Display() //定义函数模板 Display() { cout << t << endl; } template <> void Set<Date>::Display() //定制 Set 类模板的 Display()成员函数 { cout << "Date :" << t << endl; } int main() //主函数 { Set<int> instSet(123); Set<Date> dt =Date(1,2,3); instSet.Display(); dt.Display(); }程序运行结果为:
123 Date :Month:1 Day:2 Year:3程序对模板类中的 Display() 函数进行覆盖,使其参数类型设置为 Date 类,这样在使用 Display() 输出时就会调用 Date 类中的 Display() 函数。