C++语言学习07

发布时间 2023-09-02 17:29:35作者: 优秀还天籁

一、类型信息运算符
typeid
在C++中typeid可以获取数据的类型,但是需要加头文件 typeinfo
find /usr/include -name typeinfo
1、typeid是运算符,执行运算符函数,执行的返回值类型是type_info
类类型对象
2、type_info中有个name的成员函数
3、type_info中还重载了 == 运算符,可以直接比较两个数据的类型
是否相同
4、它可以区分父类指针或引用指向的是哪个子类,

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

三、为什么要使用模版
1、C/C++/JAVA/C# 属于静态编程语言(编写->预处理->编译->汇编->链接->可执行文件)-强类型语言,
静态编程语言优点是运行速度快,缺点代码改动后重新编译、实现代码的通用比较麻烦
javascript/Python/shell 弱类型语言 动态编程语言(运行速度要慢,但是不需要生成文件,可以直接运行)

C语言实现一个通用的快排
2、C语言中的通用类型是通过void* +回调函数实现的,实现难度大,使用比较麻烦
3、借助定义宏的方式实现代码通用,但是宏有一定的缺点:类型不检查,没有返回值、二义性等问题
4、借助函数重载也可以实现函数代码的通用,可能会导致代码段增多,无法解决未知类型
5、综上所诉,C++之父为了解决代码通用问题,实现了模版技术,让C++摆脱数据类型的困扰

三、函数模版
1、函数模版的定义
template
void func(T num)
{

    }
    未知类型名可以取任意名字,一般约定T

2、函数模版的原理
    函数模版会经历两次编译
    a、检查函数模版的语法是否有错误,如果无误也不生成函数的二进制
    指令,代码段没有存储该函数模版
    b、根据调用者提供的实参类型再次编译检查函数模版代码,如果也没有
    错误,才会生成一份二进制指令存储在代码段中,所以,如果函数模版没
    有任何一次调用则不会生成任何二进制指令,如果有不同类型的实参调用
    函数模版,则会生成另一份二进制指令存储在代码段
    这种函数模版实例化称为"惰性实例化"准则

3、函数模版的调用
    C++编译器不会把函数模版当作一个函数实体,而是当作生成函数实体的工具,
    当调用函数模版并提供了实际类型参数后,才会生成函数实体
    
    调用函数模版必须提供相应数量的类型参数
    自动:编译器会自动根据实参的类型,获取函数模版的类型
    手动:函数名<type1,type2,...>(实参)会根据<>中提供的类型名去生成
    函数实体

4、默认形参类型
    template<typename T1,typename T2=int,typename T3=long>
    T3 函数名(T1 arg1,T2 arg2)
    {
        T3 ret = arg1+arg2;
        return ret;
    }
    函数模版的类型参数可以像普通函数的默认形参设置形参值一样去设置默认的
    形参类型。靠右原则一致,但是该语法只有C++11后才支持

    支持-std=gun++0x

5、函数模版的特化
    模版虽好但是不能直接解决所有类型的问题,有一些特殊类型与普通类型的运算
    规则不同,例如char* ,因此需要给这些特殊类型实现一个特殊版本,这种称为
    函数模版的特化
    特化的方式:
        1、通过typeid()分支判断类型执行特殊步骤
            if(typeid(T) == typeid(char*))
        2、实现一个特化的版本
        特化的格式
            template<>
            特化类型返回值 函数名(特化类型 形参名){}
    注意:
        1、特化前,必须有一个基础版本的函数模版
        2、template<>一定要加,才说明是函数模版的特化
        3、特化函数的形参基础类型要与函数模版的形参基础类型相同,否则
        报错,例如函数模版中有引用,特化中也需要有
        4、编译器会优先调用函数模版中的特化版本,因此不会与基础的函数模版冲突
        5、可以同时存在类型完全相同的普通函数、函数模版的特化、函数模版,按照普通
        ->特化->函数模版 的顺序调用
        6、普通函数无论是否被调用都会生成二进制指令,但是模版特化依然遵循"惰性实例化"
        准则,不被调用时不生成二进制指令

四、类模版
使用未知类型来设计类类型
1、类模版定义
tempname<typename T1,typename T2>
class 类名
{
T1 成员变量;
public:
T2 func(void)
{
T2 ret;
return ret;
}
};

2、类模版的使用
    类模版必须先实例化才能使用,与函数模版不同的是不支持自动实例化,
    只能显式提供类型参数手动实例化
    类名<类型参数>  对象名;
    类名<类型参数>* 对象指针 = new 类名<类型参数(构造函数的实参数);

练习:实现链式队列的类模版

3、类模版中的静态成员
    类模版中允许有静态成员,与普通类的静态成员变量一样需要在类外定义
    template<typename T>
    class 类名
    {
        static 类型名 num;
    };
    template<typename T> T 类名<T>::num = 初始数据;
    对于每个类模版实例化出来的类对象,也是共享静态成员变量

4、递归实例化
    什么类型都可以是模版的类型参数,包括类模版类型
    template<typename T>
    class A
    {
        T a;
    };

    template<typename T>
    class B
    {
        T b;
    };
    
    B<A<int>> b;    //  C++11之后才允许
    B<A<int> > b;   //  C++11之前必修加空格

5、类模版的默认参数类型
    与函数模版的默认参数类型规则一样
    template<typename T=类型名>
    class A
    {
        T a;
    };

6、类模版的局部特化
    当类模版中的成员函数不能支持所有类型时,可以针对不同类型实现类模版中的
    成员函数的特化版本,称为类模版的局部特化
    方法1:通过typeid比较类型,通过分支语句执行特殊操作
    方法2:通过实现局部特化成员函数来处理

    template<>
    int List<const char*>::find(const char* const& data)

    注意:
    1、一般在类外实现局部特化
    2、类型名的格式除了替换的typename 的类型发生替换,其余所有格式都需要一致
    ,例如常属性、引用等


7、类模版的全局特化
    为特殊类型重新特化一个类模版,称为类模版的全局特化
    template<>
    class 类名<特殊类型>
    {
        重新实现类
    }