C++ 函数模板与类模板

注意:本文中代码均使用 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 开发编译环境,如有疑问和建议欢迎随时留言。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,484评论 1 51
  • 模板是C++语言与众不同的特性,也是标准库的基础。一个模板就是一个编译器用来生成特定类类型或函数的蓝图。生成特定类...
    何幻阅读 463评论 0 0
  • C++ 模板简介 一、模板 使用模板的目的就是能够让程序员编写与类型无关的代码。 模板是一种对类型进行参数化的工具...
    MinoyJet阅读 2,325评论 0 12
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,119评论 9 118
  • 今天上午上重修课的时候 我终于取关了他删除了微信 之前不想删就想着就那么静静呆着挺好 没改聊天背景分组依然是恋人 ...
    鹿有年阅读 166评论 0 0