浅学C++(8)C++(模板)

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

二、为什么需要使用模版
    1、C/C++都是一种静态编程语言(编辑->预处理->编译->汇编->链接->可执行文件),静态编程语言的优点就是运行速度比较快,而缺点就是实现代码通用比较麻烦
    任务:实现一个通用的选择排序\快速排序
    void qsort( void *buf, size_t num, size_t size, int (*compare)(const void *, const void *) );

    2、可以借助void*+回调函数的方式实现通用代码,但是实现难度高,使用比较麻烦
    3、借助宏函数实现部分通用代码,类型检查不严格,没有返回值、容易有二义性 swap
    4、借助函数重载也可实现通用代码,增加代码段的内容,未知类型需要重载新版本
    5、综合以上原因,C++之父在C++中实现了模板技术,目的是让C++的编程彻底摆脱数据类型的困扰

三、函数模版
    1、函数模板的格式
    template<typename T1,typename T2 ...>
    T3 函数名(T1 arg1,T2 arg2)
    {
        T3 ret = arg1 + arg2;
        return ret;
    }
    可以给未知类型取任何名字,一般约定俗称写T
    一个template模版声明只能修饰一个紧跟着的函数

    2、函数模板的原理
        函数模板会经历两次编译:
        a、检查函数模板的语法是否有错误,但不会生成函数的二进制指令
        b、根据调用者提供的参数类型再次检查代码,如果没有错误才会生成二进制指令并存放到代码段,如果没有一次调用则不会生成二进制指令
        这种方式称为函数模板的"惰性实例化"准则
    
    3、函数模板的调用
        自动:编译器会根据实参的类型,自动获取类型替换模版类型
        手动:函数名<type1,type2,type3...>(实参)
        
        C++编译器不会把函数模板当成一个函数实体,相当于一个可以生成不同版本函数的模具,当给函数模板提供了实参发生调用时,才会生成函数实体
        调用函数模板生成具体函数实体的过程叫做实例化,遵循"惰性实例化"准则
    
    4、默认形参
    template<typename T1,typename T2,typename T3 = long>
    T3 函数名(T1 arg1,T2 arg2)
    {
        T3 ret = arg1 + arg2;
        return ret;
    }
    函数模板的类型参数也可以像成员函数的参数一样设置默认类型,规则与成员函数的默认形参一致,但是只有C++11语法才支持,编译需要加 -std=gnu++0x  -std=c++0x 
    
    5、函数模板的特化
        模版虽好但不是万能的,不能解决所有问题,有些特殊的类型与普通类型的运算规则不同,因此需要给特殊类型实现一个特殊版本,称为函数模板的特化版本,例如 char*
        函数模板特化格式:
        template<>
        特化类型返回值 函数名(特化类型 参数名1)
    注意:
        1、特化前,必须有一个基础的函数模板
        2、一定要加template<>,否则就变成了普通函数
        3、编译器会优先调用特化版本,不会与普通函数模板冲突
        4、可以同时存在类型相同的普通函数、模板特化版本、普通函数模板,且会优先调用普通函数
        5、普通函数无论是否被调用都会生成二进制指令,但是模板特化依然是遵循"惰性实例化"准则
        6、特化函数的形参和返回值类型的基础类型要与模版函数的基础类型要一致,否则会报错,例            
           如:函数模板中使用了引用,特化也要加引用,也即是除了被特化的类型不同,其他要一致

四、类模版
    1、类模板:是一种使用未知类型来设计类的技术
    template<typename M,typename T,typename R>
    class Test
    {
        M num;
    public:
        Test(T t){}
        R func(void)
        {
            R ret;
            return ret;
        }
    };

    2、类模板的使用:
        必须先实例化对象才能使用对应的类模板,与函数模板不同的是它不支持自动实例化,必须显示地手动实例化
            类名<类型名1,...> 对象名;    
    
    练习1:实现一个链式的队列模板

    3、类模板的静态成员
        类模板的静态成员与普通类的静态成员一样都需要在类外定义,并且在定义静态变量时需要以以下格式定义:
        template<typename T>
        class Test
        {
            static 类型名 静态成员名;
        };

    template<> 类型名 Test<实例化类型>::静态成员名 = 初始化数据;

    4、递归实例化
        什么类型都可以作为类模板的类型,包括类模版
        template<typename T>
        class A
        {
            T t;   
        };

        template<typename T>
        class B
        {
            T t;   
        };

        A<B<char> > a;  //一定要加空格
        类名<类名<类型名> > 类对象;
    
    5、类模板的默认形参
        与函数模板一样设置方法,规则也一样
        template<typename T1,typename T2=int>
        class Test
        {

        };
    
    6、类模板的局部特化
        当类模板中的成员函数不能支持所有类型时,可以针对不同的类型提供类模板成员函数的局部特化版本
        方式1:通过类外实现一个局部特化函数
        template<> 返回值 类型<特化类型>::函数名(特化类型参数)
        {
            //除了特化类型外,其他内容与普通版本一样
            // 必须类外实现
        }
        方式2:在原成员函数中,通过typeid比较模板类型是不是特殊类型,如果是,可以通过分支语句进行特殊处理,就不需要实现特化
        if(typeid(T) == typeid(const char*))
        {
            //  特殊处理
        }
        else
        {
            //  正常处理
        }
    7、类的全局特化
        为一个特殊类型的类模板实现一个全新的特化版本,称为类的全局特化
        template<>
        class Test<特殊类型>
        {
            //使用特殊类型重新实现类
        }
    8、在定义类模板和函数模板时,class可以与typename替换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值