C++基础篇:10 模板

1 什么是模板

        是一种自动生成代码的技术,这种技术能让程序员在编写代码时不需要过多地考虑数据类型,这种技术也称为泛型编程技术

2 为什么使用模版

        C/C++是一种静态编程语言(预处理->编译->汇编->链接->可执行文件),静态编程语言缺点实现通用代码比较麻烦,优点是运行速度更快

例如 void* + 回调函数

void qsort(void *base, size_t nmemb, size_t size,int (*compar)(const void *, const void *));

     base:待排序内存首地址

     nmemb:待排序元素个数

     size:待排序每个元素字节数

     compar:回调比较函数 调用者提供

① 借助回调函数+void*实现通用版本,实现难度较高、使用比较麻烦

② 借助宏函数实现通用版本,类型检查不严格、没有返回值、容易二义性 swap

③ 借助函数重载实现通用版本,导致代码段增加,未知类型无法解决

④ 综上所述,C++之父在C++中增加了模板技术,从而让C++的编程尽量不受类型困扰

3 函数模板

3.1 函数模板的定义

template<typename T1,typename T2,...>T3 函数名(T1 arg1,T2 arg2) {}

    注意:未知类型名可以取任何名字,一般约定俗成使用T为名字

        函数模板的template声明必须每个函数模板都要有

3.2 函数模板的实例化

        C++编译器不会把函数模板先生成一个函数实体,而是当编译时发生调用,才会生成具体版本的函数实体,这个过程称为函数模板的实例化

3.2.1 自动实例化

        编译器自动根据实参类型生成该版本的函数实体

3.2.2 手动实例化:

        如果编译器无法自动根据实参类型获取所有未知类型的类型,那么就需要手动提供所有未知类型

 函数名<类型名1,类型名2,类型名3>(实参1,实参2)

3.3 函数模板的原理

        函数模板被调用时会经过两次编译

        ① 检查函数模板的语法是否有错误,此时编译结束后不会生成该函数的二进制指令

        ② 当编译到调用函数模板语句时,会再次根据调用者提供的实参类型编译函数模板,如果没有错误才会生成该版本的二进制指令存储在代码段,所有如果没有调用函数模板则不会生成具体的函数实体

        这种方式称为函数模板的"惰性实例化"准则

3.4 默认参数类型

template<typename T1,typename T2=int>T2 函数名(T1 arg1,T1 arg2){ }

        函数模板的未知类型可以设置默认的参数类型,如果调用者没有提供具体的参数类型,那么按照默认参数类型来实例化函数,但是只有C++11语法标准才支持,需要增加编译参数 :

        -std=gnu++11 或者 -std=c++0x

3.5 函数模板的特化

        模板虽能解决绝大部分类型问题,但是不能解决所有类型的问题,有个别类型的处理方式与其他类型不同,因此需要给这些特殊类型实现一个特殊版本,该函数模板称为特化

template<> 返回值类型 函数名(特化类型名 形参名,...){}

注意:

        ① 特化外,还必须有一个基础的函数模板

        ② 必须加template<>,才算是函数模板特化,不然就是普通函数

        ③ 可以同时存在普通函数模板、函数模板特化、普通函数,如果类型匹配,优先调用普通函数,其次是特化版本,最后是普通函数模板

        ④ 普通函数无论是否调用都会生成二进制指令,函数模板、特化只有调用时才会生成,遵循"惰性实例化"准则

4 类模板

        类模板:是一种使用了未知类型来设计类的技术

4.1 类模板的定义

template<typename T1,typename T2,typename R>
class Test
{
    T1 num;
public:
    Test(T1 num){}
    Test(T2& that){}
    R func(void) {}
};

4.2 类模板的使用

        必须实例化后类才会生成,也遵循"惰性实例化"原则

        与函数模板不同的是,它不支持自动实例化,必须手动实例化

        类名<类型名> 对象;

        注意:使用了类模板后,所有使用到类名的位置,类名后必须加<合适的类型名>,否则编译器报错

4.3 类模板的静态成员

        类模板允许有静态成员

        类模板和普通类的静态成员都需要在类外定义

template<typename T>
class 类名
{
    static 类型名 num;  
};
template<typename T>类型名 类名<T>::num = 初始化;
template<>类型名 类名<int>::num = 初始化;

        类模板的静态成员变量在类模板定义时不会创建,是在类对象实例化时才会创建

        而普通类的静态成员变量在程序运行前,无论是否实例化对象都会创建

Test<int> t1,t2;    //t1 t2共享同一个静态成员变量
Test<char> c1,c2;   //c1 c2共享同一个静态成员变量
//  t c 之间的静态成员变量不同享

4.4 递归实例化

        类模板的未知类型可以是任意合法类型,包括类模板类型

        类名1<类名2<类型> > 对象;  // >> 之间最好空格隔开

template<typename T>
class A
{
     T num;
};
template<typename T>
class B{};
A<B<char> > a;

4.5 类模板的局部特化

        当类模板中的个别成员函数无法支持所有类型时,可以针对特殊类型实现类模板中某个成员函数的特化,称为局部特化

4.5.1 通过类模板局部特化语法

        template<> 返回值 类名<特殊类型>::成员函数(特殊类型 形参名){}

注意:类模板的局部特化函数要求特殊类型 与 类模板的原类型,除了类型名替换不一样外,其他属性(const * &)都必须完全相同,否则编译器报错

        而且局部特化函数必须类外实现

4.5.2 通过typeid比较未知类型T是否是特殊类型,后续使用分支结构来处理特殊类型

if(typeid(T) == typeid(const char*))
{
    if(0 == strcmp())
}
else
{
    if(==)
}

4.6 类的全局特化

    为了一个特殊类类型实现整个类的特化,称为类的全局特化   

template<>
class 类名<特殊类型>
{
    //  重新实现该类
};

4.7 函数模板、类模板中,class可以替换typename

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

就酱77叭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值