c++—模板(函数模板、类模板)

1. 模板是解决代码复用的最优解,其原理是为功能不变而参数数据类型不同的的程序提供一种代码共享机制,模板也是一种多态的实现,可以在编译器协助开发者生成代码,从而演化为面向模板元编程(面向编译器),是一种解决良好代码维护性、复用性的工具;

2. 在C语言中也可以进行宏定义进行函数模板,但是存在两个缺点:①不安全,因为宏定义只是简单的替换,不进行参数类型检查;②只可实现简单函数,不适合复杂函数;

3. 模板主要应用于函数模板、类模板两种情况下;

4. 函数模板:

    (1)语法

#include <iostream>
#include <string>

template<typename T>  //函数模板声明
T my_max(const T &a, const T &b)
{
    return a > b ? a : b;
}

int main()
{
    cout << my_max<int>(5,6) << endl;  //注意这里<int>指明T的类型
    return 0;
}

使用步骤:

①先声明函数模板:template<typename T>

②编写函数,注意里面的形参的类型均用T代替;

③在main函数中调用函数模板时,需要在函数名后面跟<int>或<char>等,表明本次调用的函数模板的类型是int还是char;

    (2)内部原理

①函数模板只是个模板,不能实际执行;

②在调用时,会根据指定的参数类型,依据函数模板实例化,生成模板函数;

③函数模板可以重载,条件均是前期重载定义的条件:形参表个数或者类型参数不同;

    (3)函数模板和普通函数的执行优先级

①先执行参数完全匹配的自定义普通函数;

②次之:参数匹配的模板函数;

    (4)可变参数模板:递归思路

#include <iostream>

void test()    //递归结尾,即参数为0时结束
{
}

template <typename T, typename...U>  //注意这个形式,有...
void tes(T && first, U&&... others)
{
    cout<<first<<endl;
    test(others...);
}
//可变参数函数,使用第一个参数first做一些事情后,继续递归其他参数;
//编译器内部对每一次递归调用形式(因为参数个数变化),都生成了固定的模板函数(实例化),会有很多;

5. 类模板

    (1)语法

①在类的定义前加模板声明:template<typename T>,T就是类型代名词;

②在类内所有需要用到数据类型的都用T代替;

③若成员函数在类外定义,则每个成员函数前面都要加相同的类模板声明,建议在类内定义;

④要使得成员函数的输出类型不受该类整体的类型模板声明T的限制,则可写成类的成员函数模板,使得该成员函数依据新的类型单独变化;

⑤可以利用using给模板起别名;

template <typename T>    //类模板声明
class Stack
{
public:
    Stack();
    void push(T num);
    T pop();
    
    template<typename U>    //该成员函数不受T的限制,根据实际U的类型而定
    void print(U num)
    {
        cout<<num<<endl;
    }
private:
    int top;
    T data[SIZE];
}

template void print<int>(int num);  //即针对int类型只实例化一次,只生成一个模板函数;

int mian()
{
    Stack<int>s1;    //调用形式

    return 0;
}

    (2)隐式实例化、显式实例化

①隐式实例化会降低效率,因为每次调用模板时都会生成模板函数,无论前后的类型是否一致;

②显式实例化,相当于只生成一次模板函数,后面的再调用的隐式就不会再实例化了,本质上相当于函数声明,即调用模板时,前置声明一次,如下:

template void print<int>(const int &t);详见上图;

    (3)typename作用:在类模板中或者函数模板中访问一个类或者结构体中的自定义类型的时候,用typename修饰,放置语法歧义;

6. 模板的全特化与偏特化

    (1)特化处理的目的是对特殊的类型做特殊的处理,以求达到特定类型特定的功能(例如多输出几句话、特定的处理等等);

    (2)全特化:注意在全特化的类前加template<>

//类模板(泛化)
template <typename T1, typename T2>
class A
{
    T1 data1;
    T2 data2;
};

//模板的全特化
template <>           //注意这里的全特化声明
class A<int double>   //这里也要指定特化的具体类型
{
    int data1;
    double data2;
};

    (3)偏特化示例,注意指定具体类型

//类模板(泛化)
template <typename T1, typename T2, typename T3>
class B
{
public:
    void myfunc(T2 b)
    {
        cout<<"泛化函数"<<endl;
    }
};

//偏特化
template <typename T2>    //没有偏特化的参数类型还要注明,不能丢
class B<int, T2, int>     //已经偏特化的参数类型要在类型定义后面加上具体类型
{
public:
    void myfunc(T2 b)
    {
        cout<<"偏特化函数"<<endl;
    } 
};

    (4)全特化与偏特化的区别

①全特化实际是更加具体地指明所有的参数的固定类型;

②偏特化实际是一部分参数明确了具体的类型,而不是所有的;或者所有参数都降低了类型范围,减少了系统类型推导时间,例如T→T*,或者const修饰;

③若在特化处理中出现偏特化冲突时,用全特化处理;

④一般用类型的全特化较多,函数的全特化尽量不要用,而是直接写一个自定义的普通函数;

    (5)模板元编程

        为编译器而编码,旨在利用编译期间为生成代码而进行的编程;工作原理是利用类模板,通过编译器生成各种各类的模板类;同时利用const等关键字让系统自动回收垃圾;

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值