C/C++知识点之C++语言学习(十七)——模板
小标 2019-03-14 来源 : 阅读 570 评论 0

摘要:本文主要向大家介绍了C/C++知识点之C++语言学习(十七)——模板,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。

本文主要向大家介绍了C/C++知识点之C++语言学习(十七)——模板,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。

C/C++知识点之C++语言学习(十七)——模板

一、模板简介


泛型(Generic Programming)即是指具有在多种数据类型上皆可操作的含意。 泛型编程的代表作品STL是一种高效、泛型、可交互操作的软件组件。
泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库)。其语言支持机制就是模板(Templates)。模板的核心思想是参数化类型,即把一个原本特定于某个类型的算法或类当中的类型信息抽掉,抽出来做成模板参数T。


二、函数模板


1、宏实现交换函数


定义一个交换两个数的宏


#define SWAP(t, a, b)   \
do                      \
{                       \
    t c = a;            \
    a = b;              \
    b = c;              \
}while(0);


宏代码块实现的优点是代码复用,适合所有类型;缺点是缺少类型检查。


2、函数重载实现


#include <iostream>
using namespace std;
void swap(int &a, int& b)
{
    int t = a;
    a = b;
    b = t;
}
void swap(double &a,double b)
{
    double t = a;
    a = b;
    b = t;
}
int main()
{
    int ia = 10; int ib = 20;
    swap(ia,ib);
    cout<<ia<<ib<<endl;
    double da = 10, db = 20;
    swap(da,db);
    cout<<da<<db<<endl;
    return 0;
}


函数重载实现的优点是真正进行函数调用,C++编译器进行类型检查;缺点是根据类型重复定义函数,无法代码复用。


3、函数模板


函数模板是可用不同类型进行调用的特殊函数,关键在于类型参数化。
函数模板的语法格式如下:


template<typename/class 类型参数表>
返回类型 函数模板名(函数参数列表)
{
    函数模板定义体
}


template关键字用于声明开始进行泛型编程。
typename关键字用于声明泛指类型。
函数模板可以自动推导类型进行调用,也可以显示指定具体类型进行调用。


4、函数模板实现


#include <iostream>
using namespace std;
template <typename T>
void Swap(T& a,T &b )
{
    T t = a;
    a = b;
    b = t;
}
int main()
{
    int ia = 10; int ib = 20;
    Swap(ia,ib); //Swap<int>(ia,ib);
    cout<<ia<<ib<<endl;
    double da = 10, db = 20;
    Swap(da,db); //Swap<double>(da,db);
    cout<<da<<db<<endl;
    string sa ="china"; string sb = "America";
    Swap(sa,sb);
    cout<<sa<<sb<<endl;
    return 0;
}


判断一个变量是不是指针类型示例:


template 
<typename T>
bool isPtr(T *p)
{
    return true;
}

template
<typename T>
bool isPtr(T t)
{
    return false;
}


函数模板,只适用于函数的参数个数相同而类型不同,且函数体相同的情况。如果个数不同,则不能用函数模板。


5、函数模板分析


C++编译器从函数模板通过具体类型产生不同的函数,C++编译器会对函数模板进行两次编译,一次是函数模板代码进行编译,一次是参数替换后的函数代码进行编译。


#include <iostream>

using namespace std;

template <typename T>
void Swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

class Test
{

};

typedef void (*pFuncInt)(int&, int&);
typedef void (*pFuncDouble)(double&, double&);
typedef void (*pFuncTest)(Test&, Test&);

int main(int argc, char *argv[])
{
    pFuncInt pi = Swap;//Swap<int>
    printf("0x%x\n", pi);
    pFuncDouble pd = Swap;//Swap<double>
    printf("0x%x\n", pd);
    pFuncTest pt = Swap;//Swap<Test>
    printf("0x%x\n", pt);

    return 0;
}


函数模板本身不允许隐式类型转换,因此,自动推导类型时需要严格匹配,但当显示指定类型参数时可以进行隐式类型转换。
函数模板中的返回值类型必须显示指定。



add<int>(ia,ib);


6、多类型参数函数模板


函数模板可以定义多个不同的类型参数,但无法自动推导返回值类型,可以从左向右部分指定类型参数,实际工程中将返回值作为第一个类型参数,必须显式指定。


#include <iostream>
#include <string>

using namespace std;

template
< typename T1, typename T2, typename T3 >
T1 Add(T2 a, T3 b)
{
    return static_cast<T1>(a + b);
}

int main()
{
    // T1 = int, T2 = double, T3 = double
    int r1 = Add<int>(0.5, 0.8);

    // T1 = double, T2 = float, T3 = double
    double r2 = Add<double, float>(0.5, 0.8);

    // T1 = float, T2 = float, T3 = float
    float r3 = Add<float, float, float>(0.5, 0.8);

    cout << "r1 = " << r1 << endl;     // r1 = 1
    cout << "r2 = " << r2 << endl;     // r2 = 1.3
    cout << "r3 = " << r3 << endl;     // r3 = 1.3

    return 0;
}


7、普通函数与函数模板的关系


函数模板可以被重载,C++编译器优先考虑普通函数,但如果函数模板可以产生更好的匹配,则使用函数模板,可以通过空模板实参列表限定只能使用函数模板。


#include <iostream>
#include <string>

using namespace std;

template < typename T >
T Max(T a, T b)
{
    cout << "T Max(T a, T b)" << endl;

    return a > b ? a : b;
}

int Max(int a, int b)
{
    cout << "int Max(int a, int b)" << endl;

    return a > b ? a : b;
}

template < typename T >
T Max(T a, T b, T c)
{
    cout << "T Max(T a, T b, T c)" << endl;

    return Max(Max(a, b), c);
}

int main()
{
    int a = 1;
    int b = 2;

    cout << Max(a, b) << endl;                   // 普通函数 Max(int, int)

    cout << Max<>(a, b) << endl;                 // 函数模板 Max<int>(int, int)

    cout << Max(3.0, 4.0) << endl;               // 函数模板 Max<double>(double, double)

    cout << Max(5.0, 6.0, 7.0) << endl;          // 函数模板 Max<double>(double, double, double)

    cout << Max('a', 100) << endl;               // 普通函数 Max(int, int)

    return 0;
}


三、类模板


1、类模板的定义


C++语言中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,只关注类需要实现的功能。
类模板的定义语法如下:


template <typename T>
class classname
{

};


在类声明前使用template进行标识,&lt;typename T&gt;用于说明类中使用泛指类型T。
类内定义成员函数


template<typename T>
class classname
{
public:
    void push(int size)
    {
}
}


类外定义函数


template<typename T>
void classname<T>::push(T data)
{
}


类模板实例化为模板类:



classname<double> object;


类模板是类的抽象,类是类模板的实例。


2、类模板应用


类模板只能显示指定类型参数,无法自动推导。声明的泛型类型参数可以出现在类模板的任意地方。
类模板必须在头文件中实现,不能分开实现在不同文件中。类模板的成员函数需要定义在外部定义时,每个成员函数需要加上类模板template&lt;typename T&gt;声明。
类模板适合以相同的逻辑处理不同的数据类型的数据,因此非常适合编写数据结构相关代码。


#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
template<typename T>
class Stack
{
public:
    Stack(int size)
    {
        space = new T[size];
        top = 0;
    }
    ~Stack();
    bool isEmpty();
    bool isFull();
    void push(T data);
    T pop();
private:
    T* space;
    int top;
};
template<typename T>
Stack<T>::~Stack()
{
    delete []space;
}
template<typename T>
bool Stack<T>::isEmpty()
{
    return top == 0;
}
template<typename T>
bool Stack<T>::isFull()
{
    return top == 1024;
}
template<typename T>
void Stack<T>::push(T data)
{
    space[top++] = data;
}
template<typename T>
T Stack<T>::pop()
{
    return space[--top];
}
int main()
{
    Stack<double> s(100); //Stack<string> s(100);
    if(!s.isFull())
        s.push(10.3);
    if(!s.isFull())
        s.push(20);
    if(!s.isFull())
        s.push(30);
    if(!s.isFull())
        s.push(40);
    if(!s.isFull())
        s.push(50);
    while(!s.isEmpty())
        cout<<s.pop()<<endl;
    return 0;
}


3、类模板分析


类模板通过具体类型产生不同的类,C++编译器在类模板声明的地方对类模板代码本身进行编译,在使用的地方对类模板参数替换后产生的代码进行编译。
类模板可以定义多个不同类型参数。


#include <iostream>
#include <string>

using namespace std;

template <typename T>
class Operator
{
public:
    Operator()
    {
        cout << "Operator()" << endl;
    }
    T add(T a, T b)
    {
        cout << "T add(T a, T b)" << endl;
        return a + b;
    }
    T minus(T a, T b)
    {
        return a - b;
    }
    T multiply(T a, T b)
    {
        return a * b;
    }
    T divide(T a, T b)
    {
        return a / b;
    }
};

int main(int argc, char *argv[])
{
    Operator<int> op1;
    cout << op1.add(1, 2) << endl;
    cout << op1.add(1, 2) << endl;

    Operator<string> op2;

    cout << op2.add("D.T.", "Software") << endl;

    return 0;
}
// output:
// Operator()
// T add(T a, T b)
// 3
// Operator()
// T add(T a, T b)
// 3
// Operator()
// T add(T a, T b)
// Hello World


上述代码中,类模板中的函数代码在使用的时候才会被分别编译。


四、模板的特化


1、类模板的特化


类模板可以被特化,以下情况需要特化类模板:
A、指定特定类型的实现
B、部分参数类型必须显示指定
C、根据类型参数分开实现类模板
类模板的特化分为部分特化和完全特化。部分特化是指用特定规则约束类型参数,完全特化是指完全显示指定类型参数。
类模板的特化是模板的分开实现,本质上是同一个类模板,特化类模板必须显示指定每一个类型参数。编译器会自动优先选择特化类模板。


#include <iostream>

using namespace std;

template
<typename T1, typename T2>
class Test
{
public:
    void add(T1 a, T2 b)
    {
        cout << "void add(T1 a, T2 b)" << endl;
        cout << a + b << endl;
    }
};

//部分特化
template
<typename T>
class Test<T,T>
{
public:
    void add(T a, T b)
    {
        cout << "void add(T a, T b)" << endl;
        cout << a + b << endl;
    }
    void print()
    {
        cout << "class Test <T,T>" << endl;
    }
};

//完全特化
template
<>
class Test<int,int>
{
public:
    void add(int a, int b)
    {
        cout << "void add(int a, int b)" << endl;
        cout << a + b << endl;
    }
    void print()
    {
        cout << "class Test<int,int>" << endl;
    }
};

int main(int argc, char *argv[])
{
    Test<int, int> t1;//完全特化
    t1.add(1,2);
    t1.print();

    Test<double, double> t2;//部分特化
    t2.add(3.14,2.0);
    t2.print();

    Test<float, double> t3;//类模板
    t3.add(3.14,2.0);

    return 0;
}

// output:
// void add(int a, int b)
// 3
// class Test<int,int>
// void add(T a, T b)
// 5.14
// class Test <T,T>
// void add(T1 a, T2 b)
// 5.14


2、函数模板的特化


函数模板只支持模板的完全特化。


#include <iostream>

using namespace std;

//函数模板
template
<typename T>
bool Equal(T a, T b)
{
    cout << "bool Equal(T a, T b)" << endl;
    return a == b;
}

//函数特化模板
template
< >
bool Equal<double>(double a, double b)
{
    const double delta = 0.00000000000001;
    double r = a - b;
    cout << "bool Equal<double>(double a, double b)" << endl;
    return (-delta < r) && (r < delta);
}

//函数重载
bool Equal(double a, double b)
{
    const double delta = 0.00000000000001;
    double r = a - b;
    cout << "bool Equal(double a, double b)" << endl;
    return (-delta < r) && (r < delta);
}

int main(int argc, char *argv[])
{
    Equal<double>(0.1,0.1);//函数特化模板
    Equal<int>(10,10);//函数模板
    Equal(0.1,0.1);//函数重载

    return 0;
}

// output:
// bool Equal<double>(double a, double b)
// bool Equal(T a, T b)
// bool Equal(double a, double b)


工程实践中当需要重载函数模板时,优先使用函数模板特化,当函数模板特化无法满足需求时,使用函数重载。


五、数组类模板


1、数值型模板


模板参数可以是数值型参数,数值型模板参数存在限制:
A、变量不能作为模板参数
B、浮点数不能作为模板参数
C、类对象不能作为模板参数
模板参数是在编译阶段处理的,因此在编译阶段需要唯一确定。
使用最高效方式求1+2+3+4......+100


#include <iostream>
using namespace std;
template
<int N>
class Sum
{
public:
    static const int value = Sum<N-1>::value + N;
};

template
<>
cla    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程