C++ 函数模板与类模板

作者: 赵者也 | 来源:发表于2017-04-14 08:44 被阅读172次

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

相关文章

  • 10-C++远征之模板篇-学习笔记

    C++远征之模板篇 将会学到的内容: 模板函数 & 模板类 -> 标准模板类 友元函数 & 友元类 静态数据成员 ...

  • 慕课网-C++远征之模板篇(上)-学习笔记

    C++远征之模板篇 将会学到的内容: 模板函数 & 模板类 -> 标准模板类 友元函数 & 友元类 静态数据成员 ...

  • C++ 模板开发

    C++模板开发分为两类: 模板函数开发 模板类开发 模板函数语法: template是关键字,

  • Geekband C++ 第五周

    概述 C++模板简介 函数模板 C++类模板 操作符重载 泛型编程 容器

  • [GeekBand] STL与泛型编程-1

    迭代器(iterator)C++中的类模板(class template)与函数模板(funtion templa...

  • 【C++ Templates(11)】深入模板基础

    参数化声明 C++现在支持四种基本模板:类模板,函数模板,变量模板(variable template),别名模板...

  • C++类模板

    一、定义 C++ 类模板 定义一个 C++ 类模板和定义一个函数模板类似,可以指定一个或者多个模板参数标识符。在类...

  • C++ STL初识及整理

    概述 简介 简单介绍:C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些...

  • C++模板编程/泛型编程

    模板分类 模板分为函数模板与类模板两类。 函数模板 模板声明 模板定义 例如 函数模板实参类型不一致问题 参数推导...

  • 模板与泛型 —— 可变参模板

    一、可变参函数模板 二、可变参类模板 C++ 11 中引入了 可变参模板 (Variadic Template):...

网友评论

  • Vincent_Jiang:博主用的什么IDE:heart_eyes:
    赵者也:我用的是 Qt Creator 详细的可以看地址 https://www.qt.io/ 免费的,推荐了解和学习 Qt 开发框架

本文标题:C++ 函数模板与类模板

本文链接:https://www.haomeiwen.com/subject/seeiattx.html