目录
3、一个不常见的C++语法结构:半角冒号用于构造函数里的初始化列表开始标识符
定制模板类后,如果需要扩展模板心累的功能,就要对类模板进行覆盖,使模板类能够完成特殊的功能。
覆盖操作可以针对整个类模板、部分类模板以及类模板的成员函数,这种覆盖操作称为定制。
这个过程以案例说明。
一、定制一个类模板,覆盖类模板中定义的所有成员。
1、简述这项工作的大概过程
(1)、定义普通类
- 定义一个普通类class Date,这个类有3个数据参数int iMonth,iDay,iYear;
- 在这个普通类里定义构造函数、友元重载运算符<<函数、显示输出函数;
(2)、定义模板类
- 定义模板类template <class T> class Set 其中参数t是T类型,即标准数据类型;
- 定义构造函数并对构造函数初始化。t是形参,数据类型是T,用实参st实例化,当然也可以用intset、dt等实例化;
- 显示输出函数;
(3)、定制模板类
- 定制模板类、扩展、专业化模板类template<> class Set<Date>,用Date类型替换T类型;
- 覆盖 class Set 全部类体;
(4)、main函数
- Set<int> intset(123);
- 实例化说明:定义模板类对象,数据类型int(注意用int代替T),定义class Set对象变量intset(用intset代替形参t),给实参intset赋值123并代替形参st;
-
Set<Date> intset1(123);
-
实例化说明:定制模板类对象,数据类型Date,定义class Set<Date>对象变量intset1(用intset1代替形参t),给实参intset1赋值123并代替形参st;
2、定制模板的方法
对模板类的成员函数的特化时要加上template<>;对模板类的成员函数的特化,需要返回值类型与模板匹配;
template <class T> class Set { T display(); }; //声明
template<> class Set<Date> { Date display(); }; //特化
3、一个不常见的C++语法结构:半角冒号用于构造函数里的初始化列表开始标识符
这个语法结构在一般的教科书里看不到解释,个别的书里有使用但没有解释。这个语法结构是:半角冒号用于构造函数里的初始化列表开始标识符。
Set(Date st):t(st){}; //这时定义在类里面,如果定义在外面,前面还会有个 set::
//等价与
set(data st)
{
t = st;
}
两种用法是等价的,第二种用法更容易理解。
区别在于,第二种形式 t会多执行一步默认构造函数构造t,再在构造函数体语句块中调用赋值操作符给t再赋一次值。
另外,对于一些特殊情况必须写在初始化列表里,如引用型属性的初始化。因为那才是初始化,语句块中的是初始化后的再赋值。
4、整个过程的实例
/*定制一个类模板,覆盖类模板中定义的所有成员*/
#include <iostream>
using namespace std;
//定义普通类
class 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)//定义一个友元重载运算符‘<<’用于输出Date类型
{
cout << "Month: " << t.iMonth << ' ' ;
cout << "Day: " << t.iDay<< ' ';
cout << "Year: " << t.iYear<< ' ' ;
return os;
}
void Display() //定义成员函数
{
cout << "Month: " << iMonth;
cout << "Day: " << iDay;
cout << "Year: " << iYear;
cout << endl;
}
};
//定义类模板,共用一个类模板
template <class T>
//定义模板类Set,其中参数t是int类型,不能识别Date类型,输入Date类型,警告E0289
class Set
{
T t; //定义类变量
public:
Set(T st) : t(st) {} //定义构造函数,冒号是构造函数里的初始化列表开始的标识Set(T st) {t = st;}
//t是形参,数据类型是T,用实参st实例化,当然也可以用at、mt实例化
void Display() //定义成员函数
{
cout << t << endl;
}
};
//定制新的模板类Set<Date>,覆盖原类参数,共用一个类模板<class T>
//其中参数t是Date类型
//必须增加template<>否则警告C2906: “Set<Date>”: 显式专业化需要“template <>”
template<> class Set<Date>
{
Date t; //定义类变量
public:
Set(Date st): t(st){} //定义构造函数,冒号是构造函数里的初始化列表开始的标识Set(Data st) {t = st;}
//t是形参,数据类型是Data,用实参st实例化,当然也可以用at、mt实例化
void Display() //定义成员函数
{
cout << "Date :" << t << endl;
}
};
int main()
{
Set<int> intset(123);
intset.Display();
//注意到上面<int>如果替换为<Date>结果就不一样了,再把实参修改为Date类型
Set<Date> intset1(123);
intset1.Display();
Set<Date> intset2(Date(04, 20, 2023));
intset2.Display();
Set<Date> dt (Date(04,20,2023));
dt.Display();
Set<Date> dt1=Date(04, 21, 2023);
dt1.Display();
}
/*运行结果
123
Date :Month: 123 Day: 0 Year: 0
Date :Month: 4 Day: 20 Year: 2023
Date :Month: 4 Day: 20 Year: 2023
Date :Month: 4 Day: 21 Year: 2023 */
5、按单步调试程序,注意并观察实参与形参的匹配
二、定制一个类模板,覆盖类模板中定义的指定的成员。
1、定制模板覆盖指定的部分成员的方法
用类型替换定制模板,用作用域::覆盖指定的成员函数。一般表达式是:
void Set<T>::Display() //定制新类并覆盖部分成员函数
{
cout << t << endl;
}
void Set<Date>::Display() //再定制新类并覆盖部分成员函数
{
cout << "Date: " << t << endl;
}
这里只列出了定制类的方法,源类定义详见下面的源码。一般表达式的含义是,定制类分别用实参类型T(基本类型)和实参类型Data(日期类型)代入源类形参,然后根据实参类型的实例化选择对应的定制类输出并显示结果。
2、整个过程的实例
/*定制一个类模板,覆盖类模板中定义的指定的成员*/
#include <iostream>
using namespace std;
class Date //定义普通类
{
int iMonth,iDay,iYear;
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)//定义有缘运算法<<用于Display(Date)
{
cout << "Month: " << t.iMonth << ' ' ;
cout << "Day: " << t.iDay<< ' ';
cout << "Year: " << t.iYear<< ' ' ;
return os;
}
void Display() //定义成员函数
{
cout << "Month: " << iMonth;
cout << "Day: " << iDay;
cout << "Year: " << iYear;
cout << std::endl;
}
};
template <class T> //定义类模板,共用一个类模板
class Set //定义模板类Set,其中参数t是int类型,不能识别Date类型,输入Date类型,警告E0289
{
T t;
public:
Set(T st) : t(st) {} //定义构造函数,冒号是构造函数里的初始化列表开始的标识Set(T st) {t = st;}
void Display(); //定义空成员函数
};
template <class T> //不可以注释掉
//覆盖类模板中的部分成员:类作用域::成员函数
void Set<T>::Display() //定制新类并覆盖部分成员函数
{
cout << t << endl;
}
void Set<Date>::Display() //再定制新类并覆盖部分成员函数
{
cout << "Date: " << t << endl;
}
int main()
{
Set<int> intset(123); //调用class Set,实参int代入形参T,对象变量intset代入形参st,对象实例123代入形参t,完成实例化
intset.Display(); //调用第一个覆盖模板类的成员函数void Set<T>::Display()
Set<Date> dt =Date(14,20,2023); //调用class Date的构造函数->赋值->class Set构造函数
dt.Display(); //调用第二个覆盖模板类的成员函数void Set<Date>::Display()->友元重载运算符<< ->void Set<Date>::Display()->输出t
}
/*运行结果:
123
Date: Month: 14 Day: 20 Year: 2023 */
3、按单步调试程序,注意并观察实参与形参的匹配;
三、总结
根据实参类型定制、特化新类,并根据实参实例化选择对应的类输出结果,是模板类定制的核心奥义。