首页 > 编程笔记 > C++笔记 阅读:49

C++类模板用法详解(附带实例)

C++ 中,关键字 template 不但可以定义函数模板,还可以定义类模板。

我们定义过很多类,每个类的成员变量及成员函数的参数、返回值类型都是确定的。类模板的作用则是,类中的成员变量及成员函数的参数、返回值类型不具体指定,编译时自动根据传入的数据类型进行判断。

本质上,类模板是用类生成类,能有效减少类的定义数量。

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() 函数。

相关文章