C++泛型编程--模版
这里写目录标题模板特点函数模版语法注意事项普通函数与函数模版的区别普通函数与模版函数调用规则模板的局限性类模板语法类模板和函数模板的区别类模板中成员函数的创建时机类模板对象做函数参数类模板与继承补充待补充类模板成员函数的类外实现类模板的分文件编写PShpp使用注意事项重要类模板与友元模板案例 自制数组需求分析知识点模板特点函数模版语法最上面一行是在声明 我要创建模板了 其中T可以替换 但是一般都用T 因为他是template的首字母之后 进行函数定义与声明使用背景当多个函数的形式一致时如上图 两个函数的框架基本上一样 只是一些类型说明不一样 就可以使用模板 提高代码的复用性代码示例首先声明模版 并声明且定义函数模版 注意 这些声明都是在main函数外面 且typename可以用class替换之后在调用函数时 有两种方式1、系统自动推导类型 直接传入实体变量 之后系统会根据实体变量的类型去推导T的类型2、显示指定类型直接在函数名后面加尖括号数据类型 在尖括号里声明数据类型即可注意事项对于自动类型推导 使用时必须是一致的数据类型 比如传参的时候 要传入数据类型一致的变量 否则会报错当我们声明了模版并且声明了一个返回值是void且参数列表为空的函数的时候 即使整个函数没有用到T 那调用的时候也要显示出T的数据类型 此时不可以再使用自动类型推导 而是使用显示指定类型的方式 随便指定一种数据类型注意 一个模版声明只能对应一个函数 也就是有几个模版函数 那么就有几个模板声明 每个函数声明的上面都要声明模版普通函数与函数模版的区别1、普通函数 可以发生隐试类型转换 也就是某些情况下可以发生自动类型转换 比如下面这种 char类型自动转换为int型2、对于函数模版如果使用自动类型推导 不会发生隐式转换 可能受限于自动类型转换时的规定T必须保持一致 因为系统要根据传入的数据去推导T但是如果使用显示指定类型 那么可以发生隐式类型转换 当使用显示指定类型时 就可以视为是在使用普通函数 因为已经指定好了T普通函数与模版函数调用规则1、对于第一点 如果普通函数没有实现 只有声明 那么并不会去调用函数模版 而是去报错没有函数实现2、通过空模版参数列表可以调用函数模版空模版参数列表 类似于显示指定类型的调用格式 只不过里不写任何内容3、函数模版也可以发生重载4、如果让编译器选择调佣普通函数进行隐式转换还是选择调用函数模板推导T 那么编译器会选择后者总结模板的局限性问题背景解决方案普通的函数模版 无法用于自定义的数据类型 这时候有两种解决方案 一种是实现运算符重载 第二种是实现模版的具体化重载 将原本的模板声明加与函数声明二合一 将template 加在函数名之前 之后 参数列表里的数据类型写明为自定义数据类型 函数体里进行重载即可 这样原先的函数模版仍然可以用 而自定义数据类型 也有了自己专属的模板函数模板的具体化与重新写一个函数的区别在于 具体化模板相当于将普通函数进行了重载 保证了函数调用时函数名的统一性 总结所以有可能系统为了函数调用名的统一性 而不去使用新建一个函数 而是用模板的具体化重载 学习该知识点可能我们用不到 但是可以读懂系统的模板库类模板语法仍然是先声明模板注意声明模板时 要声明两个虚拟类型T 并且分别命名 前面有说过 虚拟类型的名字可以自定义之后 在类中就分别用虚拟类型去写这个类调用时 要在数据类型之后紧跟模板参数列表 指明虚拟类型是什么 可以将此操作与类名一起 视为一个数据类型类模板和函数模板的区别在定义时的模板参数列表中 定义参数的默认值即可1、类模板不能使用自动类型推导的方式2、定义了模板参数的默认值之后 调用时 模板参数列表默认参数的位置就可以不写了类模板中成员函数的创建时机类模板中的成员函数 在调用某个成员函数时 才去创建某个成员函数 不调用某个成员函数 那么某个成员函数就不会被创建代码示例如图test函数 调用时 参数列表写Person1 创建了MyClass类的对象 m 那么MyClass类中的T全换成了Person1之后m对象调用func1 不去调用func2 没有出错m对象调用func2 出错因为类模板的成员函数只有被调用时才会创建 所以没有调用func2时 func2就不会被创建 相当于不存在 也就是func2的错误不会被发现但是一旦调用了func2 那么类中的成员函数就会被创建 他的错误就被发现了 编译器报错类模板对象做函数参数当类模板当做函数参数的时候的语法首先定义一个类模板1、对于第一种方式 指定传入类型 也就是设置函数时 指定好对象的具体实例化的类型就可以了 最好以引用的方式传入 这样节省空间 并且形参与实参有联系2、参数模板化在定义函数的时候 也将对象的类型模板化 同时函数上一行要加上模板的声明 并定义模板变量3、整个类模板化 不需要对模板的具体类的参数模板化 直接将Personstring ,int 这一整套当做一个数据类型 将其模板化 同样提供模板声明以引用传递类模板与继承当父类是一个模板类 那么子类应该指定出父类中T的类型 才可以正常操作 这个指定可以定死 也可以模板来定可以在子类继承的时候 直接定死父类的模板参数列表弊端是 这样父类就受到了局限可以将子类也变成一个类模板 之后继承声明时在父类的参数列表里传入T这里的T可以随意命名 不一定与上面的T命名一致之后子类创建对象时 就要在模板参数列表传入具体的数据类型补充待补充typeid 关键字参数 虚拟类型函数 name 功能显示数据类型的名称类模板成员函数的类外实现首先是一个类模板的定义注意点类模板中的成员函数只写声明 不写实现分别是构造函数的实现以及成员函数的实现实现步骤与普通的构造函数、成员函数差不太多不同的是 对于普通的函数 写作用域时只需要写类名即可但是模板函数在类名后面要加上参数列表 并写上类已经定义好的虚拟数据类型最后再在上一行加上模板声明以及虚拟数据类型的定义或者 还是那句话 可以将PersonT1,T2 看成一个新的数据类型 就理解为类模板类型类模板的分文件编写首先明确分文件编写 就是.h头文件里面写声明 进行文件引用.cpp文件写类外实现 并进行文件引用问题像以往那样分文件编写 编译可以通过 但是运行时会报错 因为如果只包含.h头文件 由于类模板的成员函数只有在运行调用时才会被创建 所以 不运行就无法看到成员函数 但是在编译之后以及在运行之前 还有个链接阶段 在该阶段会将main函数里的函数调用与函数链接起来 首先编译没有发现问题 因为没有函数也不会编译出错 然后又因为没有进入运行阶段 所以函数也没有被创建 那么链接阶段就会报错 所以会出问题解决方法一 不包含.h头文件 而是包含.cpp实现文件 由于.cpp文件中包含了.h文件 所以 包含.cpp文件 编译器并不会只看到.h 而是看到了所有的内容 这样 编译器虽然仍然不会去创建函数 但是知道该函数的存在 知道了存在 就可以链接到 就可以运行了并不常用 一般用第二种方法第二种方法将类外实现跟实现声明 写在一个文件里 并以hpp为后缀 之后mian函数直接包含.hpp文件即可 这样同样编译器看到了函数的声明与实现 但不会去创建函数 但是知道了他的存在 就可以通过链接阶段 进入运行阶段PShpp使用注意事项重要直觉上hpp是可以进行“实现”的头文件但是注意头文件要被多处包含如果你在头文件里进行了实现现在又多处包含因为头文件最终会展开到包含他的文件内即复制粘贴最终会导致该定义出现在多处最终编译会报错“重复定义”而hpp中可以进行“实现”的情况是1、模版函数或者模版类可以在hpp内进行函数实现这是特殊机制2、前面标注了inline内联函数可以在hpp内进行实现因为 inline 函数允许定义出现在多个翻译单元里链接时会合并。3、类中的函数可以在hpp中进行函数实现因为他们天然inline不建议这样这样的话.cpp中就不能进行类的函数实现了否则都不用多处包含头文件直接.hpp与.cpp就会导致报错重定义4、其他情况下不可以在hpp中进行函数实现否则多处包含头文件会导致多处复制粘贴导致重定义而.h中可以进行“实现”的情况是2、前面标注了inline内联函数可以在hpp内进行实现因为 inline 函数允许定义出现在多个翻译单元里链接时会合并。3、类中的函数可以在hpp中进行函数实现因为他们天然inline不建议这样这样的话.cpp中就不能进行类的函数实现了否则都不用多处包含头文件直接.h与.cpp就会导致报错重定义4、其他情况下不可以在h中进行函数实现否则多处包含头文件会导致多处复制粘贴导致重定义所以hpp与h没啥区别而基于此开发规约是模版就用hpp同时进行模版函数声明与模版函数实现其他情况均用h类模板与友元类模板于友元配合 可以实现带模板参数的全局函数的编写首先明确 全局函数可以在类内实现 直接在一个类里 写全局函数 之后在前面加一个friend即可 这样该函数就是全局函数首先是第二种方式1、全局函数的声明还是在类内 并且有friend关键字2、全局函数的实现全局函数的类外实现 由于是类模板 比较特殊 应该在类的上方进行全局函数的实现 并且加上模板声明 因为函数声明里有模板参数 其次 还要在该实现的上方 声明存在Person类 也是需要声明模板 之后声明类该方式不推荐使用第一种方式 直接在类内进行全局函数的声明 以及实现 之后在函数前加上一个friend关键字即可 这样的函数就是一个全局函数模板案例 自制数组需求分析知识点this指向当前调用该成员函数的对象 *this 也就是当前调用的对象