C++ 函数模板与类模板(使用 Qt 开发编译环境)

注意:本文中代码均使用 Qt 开发编译环境,如有疑问和建议欢迎随时留言。

模板是 C++ 支持参数化程序设计的工具,通过它可以实现参数多态性。所谓参数多态性,就是将程序所处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。

函数模板

函数模板的定义形式是:

template <class T> // or template <typename T>
returnType functionName ( params ) {
       // todo something
}

所有函数模板的定义都是用关键字 template 开始的,该关键字之后是使用尖括号 <> 括起来的类型参数表。每一个类型参数 T 之前都有关键字 class 或者关键字 typename,这些类型参数代表的是类型,可以是内部类型或自定义类型。这样,类型参数就可以用来指定函数模板本身的参数类型和返回值类型,以及声明函数中的局部变量。函数模板中函数体的定义方式与定义其它函数类似。

实例一

#include <QCoreApplication>
#include <QDebug>

template < typename T >
T abs(T x) {
    return x < 0 ? -x : x;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    int n = -5;
    double d = -5.5;
    qDebug() << abs(n);
    qDebug() << abs(d);
    qDebug() << abs(d+n);

    return a.exec();
}

运行结果:


运行结果

实例二

#include <QCoreApplication>
//#include <QDebug>
#include <iostream>
using namespace std;

template <typename T>

void outputArray(const T *P_aaray,const int count){
    for(int i=0; i < count; i++) {
        cout<<P_aaray[i]<<" ";
    }
    cout<<endl;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    const int aCount = 8, bCount= 8, cCount = 20;
    int aArray[aCount] = {1,2,3,4,5,6,7,8};
    double bArray[bCount] = {1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8};
    char cArray[cCount] = "Welcometo see you!";

    cout <<"a Array contains: "<<endl;
    outputArray(aArray,aCount);

    cout <<"b Array contains: "<<endl;
    outputArray(bArray,bCount);

    cout <<"c Array contains: "<<endl;
    outputArray(cArray,cCount);

    return a.exec();
}

运行结果:


运行结果

函数模板几点注意

① 如果在全局域中声明了与模板参数同名的对象函数或类型,则该全局名将被隐藏。例如,在下面的例子中tmp 的类型不是 double 而是模板参数 Type :

typedef double Type;

template <class Type>
Type min( Type a, Type b ) {
    // tmp 类型为模板参数 Type
    // 不是全局 typedef
    Type tmp = a < b ? a : b;
    return tmp;
}

② 在函数模板定义中声明的对象或类型不能与模板参数同名

template <class Type>
Type min( Type a, Type b ) {
    // 错误: 重新声明模板参数 Type
    typedef double Type;
    Type tmp = a < b ? a : b;
    return tmp;
}

③ 模板类型参数名可以被用来指定函数模板的返回位

// ok: T1 表示 min() 的返回类型,T2 和 T3 表示参数类型
template <class T1, class T2, class T3>
T1 min( T2, T3 );

④ 模板参数名在同一模板参数表中只能被使用一次,但是模板参数名可以在多个函数模板声明或定义之间被重复使用

// 错误: 模板参数名 Type 的非法重复使用
template <class Type, class Type>
Type min( Type, Type );

// ok: 名字 Type在不同模板之间重复使用
template <class Type>
Type min( Type, Type );
template <class Type>
Type max( Type, Type );

⑤ 如果一个函数模板有一个以上的模板类型参数则每个模板类型参数前面都必须有关键字 class 或 typename

// ok: 关键字 typename和 class 可以混用
template <typename T, class U>
T minus( T*, U );

// 错误: 必须是 <typename T, class U> 或 <typename T, typename U>
template <typename T, U>
T sum( T*, U );

⑥ 为了分析模板定义,编译器必须能够区分出是不是类型的表达式。对于编译器来说它并不总是能够区分出模板定义中的哪些表达式是类型(例如:如果编译器在模板定义中遇到表达式 Parm::name 且 Parm 这个模板类型参数代表了一个类那么 name 引用的是 Parm 的一个类型成员吗?)

template <class Parm, class U>
Parm minus( Parm* array, U value ) {
    Parm::name * p; // 这是一个指针声明还是乘法乘法?
}

编译器不知道 name 是否为一个类型。因为它只有在模板被实例化之后才能找到 Parm 表示的类的定义。为了让编译器能够分析模板定义,用户必须指示编译器哪些表达式是类型表达式。告诉编译器一个表达式是类型表达式的机制是在表达式前加上关键字 typename 例如如果我们想让函数模板 minus() 的表达式 Parm::name 是个类型名因而使整个表达式是一个指针声明我们应如下修改:

template <class Parm, class U>
Parm minus( Parm* array, U value ) {
    typename Parm::name * p; // ok: 指针声明
}

如上面的几个例子中所示,关键字 typename 也可以被用在模板参数表中以指示一个模板参数是一个类型。
⑦ 如同非模板函数一样,函数模板也可以被声明为 inline 或 extern 的。此时,应该把指示符放在模板参数表后面而不是在关键字 template 前面。

// ok: 关键字跟在模板参数表之后
template <typename Type>
inline 
Type min( Type, Type );

类模板

使用类模板使用户可以为类声明一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值能取任意类型(包括系统预定义的和用户预定义的)。由于类模板需要一种或多种类型参数,所以类模板也常称为参数化类。

类模板声明的语法形式是:

template<模板参数表>
class类名{
    //类成员声明
}

如果需要在类模板以外定义其成员函数,则需要采用以下形式:

template<模板参数表>
类型名类名<T>::函数名(参数表)

“模板参数表”由用逗号分隔的若干类型标识符或常量表达式构成,其内容包括:
(1)class 或 typename 标识符,指明可以接受一个类型的参数。
(2)类型说明符 标识符,指明可以接受一个由“类型说明符”所规定类型的常量作为参数。
“模板参数表”同时包含上述多项内容时,各项内容以逗号分隔。应该注意的是,模板类的成员函数必须是模板函数。

一个类模板声明自身不产生代码,他说明了类的一个家族。只有当它被其它代码引用时,模板才根据引用的需要产生代码。

使用一个模板类来建立对象时,应按如下形式声明:

模板<模板参数表> 对象名1,…,对象名n;

使用实例:

#include <QCoreApplication>
#include <QDebug>

struct Student {
    int id;
    float gpa;
};

template <class T>
class Store{
public:
    Store(void);

    T GetElem(void);
    void PutElem(T x);

private:
    T item;
    int haveValue;
};

template <class T>
Store<T>::Store(void)
    : haveValue(0)
{
}

template <class T>
T Store<T>::GetElem(void) {
    if (haveValue == 0) {
       qDebug() << "No item present!";
       exit(1);
    }
    return item;
}

template <class T>
void Store<T>::PutElem(T x){
    haveValue++;
    item = x;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Student g ={1000,23};

    Store<int> S1,S2;
    Store<Student> S3;
    Store<double> D;

    S1.PutElem(3);
    S2.PutElem(-7);

    qDebug() << S1.GetElem() << " " << S2.GetElem();

    S3.PutElem(g);

    qDebug() << "The student id is "<< S3.GetElem().id;
    qDebug() << "Retrieving object D ";
    qDebug() << D.GetElem();

    return a.exec();
}

输出结果:


输出结果

注意:本文中代码均使用 Qt 开发编译环境,如有疑问和建议欢迎随时留言。



作者:赵者也
链接:http://www.jianshu.com/p/4c64d1b567a7
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

https://www.cnblogs.com/aademeng/articles/7265011.html

posted @ 2018-10-24 18:49  findumars  Views(5481)  Comments(1Edit  收藏  举报