• C++征途 --- 模板 --- 类模板


    第一部分 --- 类模板基本语法

     说白了就是类中的成员的类型可以用泛型变量T替代

     

     补充知识:模板声明中,我们可以在两个箭头中声明多个泛型变量,注意变量名要不一样,然后变量类型可以是typename,也可以是class,变量与变量之间要用逗号隔开

    如何调用类模板来实例化一个对象呢?

    语法:类模板的类名<给泛型参数赋予具体的类型> 对象名  (上面那个括号是构造函数的需要) 


    第二部分 --- 类模板与函数模板的区别

     1.类模板只有显示指定类型的使用方式,没有自动类型推导的使用方式

     上面这个使用自动类型推导的类模板就报错了 ,使用显示指定类型的类模板就没问题

    2.类模板在模板参数列表中可以有默认参数

    首先类模板/函数模板的参数列表在哪?

    template 后面跟着的<>里面就是模板参数列表

    当这个模板参数列表属于类模板的时候,模板参数列表中能够有默认参数,如上图我们可以默认一个泛型参数为int型,如果调用类模板在调用的时候没有给这个有默认类型的泛型参数赋值的话,那这个泛型参数表现的就是默认参数的性质,如果有给他赋值的话则表现的是新赋值的类型的性质

    创建模板默认参数的语法  : 泛型类型 参数名 = 默认类型

     

     注意!!只有类模板的模板参数列表中能够有默认参数,函数模板则不行!!!


    第三部分 --- 类模板中成员函数创建时机

     通过普通类实例化的对象,它的成员函数会在对象实例化的时候被创建好

    但是通过类模板实例化的对象,它的成员函数只有在被对象调用的时候才会临时创建,然后被拿来进行调用。


    第四部分 --- 类模板对象做函数参数

     

    接受类模板实例化的对象的第一种方式 ---- 指定传入类型 

    首先我们用引用的方式来接收这个对象,然后我们直接指定这个对象的模板参数的类型 ,如上图

    指定模板参数的类型然后用引用的方式来接收。这种就叫做指定传入类型

     就是直接将接收参数的函数变为函数模板来接收类模板实例化的对象

    (补充知识,如何得知在函数模板中,编译器自动推导出的模板参数的类型是什么?方法就是调用 typeid 方法并将模板参数最为参数传给函数,然后再用点操作符调用name获取模板参数类型名)

     三种方法:

    1.直接指定法 --- 类确定,类中的模板参数类型也确定,在调用函数之前就指定好了模板参数列表中的各个参数的类型

    2.参数模板化 --- 类确定,但是类中的模板参数类型不确定,我们将其模板化,将类中模板函数,等模板参数传入的时候再确定类中的模板参数类型

    3.整个类模板化 --- 是第二种方法的变种,类不确定,模板参数不确定,直接将传进来的对象的类模板化,传进来的时候再确定这个类(此时函数也变为了函数模板)

    一般来说第一种方法(直接确定类和参数类型)用的比较多,后面两种都是函数模板配合类模板的组合使用了

     


    第五部分 --- 类模板与继承

     

    最关键的一点是要给父类的模板参数指定一个类型,指定语法

    父类类名<指定给父类模板参数中的泛型参数的类型>

    然后指定给父类泛型参数的类型可以是另一个泛型参数

    所以我们也可以将子类变为类模板,然后在模板参数列表中创建一个新的 泛型参数,并把这个泛型参数指定给父类


    第六部分 --- 类模板成员函数类外实现

    复习:成员函数的类外实现:在类中我们只写成员函数的声明,成员函数的定义(实现)放到类外写:

    1.类模板构造函数的类外实现

     其实不如说是类模板中的函数模板的实现

    首先我们要告诉计算机下面这个函数实现是属于一个模板的,以及这个模板的参数列表是什么

    template <模板参数列表>

    然后我们要告诉编译器这个函数是属于那一个模板的

    Person(类模板模板名)<类模板的模板参数名>::

    最后我们要开始写我们的函数定义(实现)

    2.类模板普通成员函数的类外实现

    其实没有什么区别,就是多了一个返回类型在作用域前面 


    第七部分 --- 类模板份文件编写

     出现问题的原因:在进入编译的时候,当函数声明与函数实现在不同文件的时候,函数的声明会自动跨文件去链接对应的函数实现,这样当我们只包含具有函数声明的头文件的时候,也能直接去调用函数了(当我们调用函数的时候就会通过函数声明找到它链接到的函数实现,然后进行函数调用)

    但是问题是,类模板中的成员函数都是在程序运行时被调用的时候才进行创建,这就导致在编译阶段我们的函数声明是无法跨文件链接到对应的函数实现的(根本就找不到函数),此时如果在只包含函数声明的文件中通过函数声明调用函数的话,程序会报出链接失败的错误,这个错误的意思就是函数无法链接到对应的函数实现,导致函数调用失败

    解决问题的方法有两种:

    1.直接在调用函数的文件中包含具有函数实现的.cpp文件,但是这种方式会降低代码的复用性

    所以我们一般选择第二种解决方法

    2.对于类模板的成员函数的实现和成员函数的声明 --- 说白了就是直接将类模板写到另一个文件中,然后这个文件的后缀名是.hpp,函数模板同理

     大家看到.hpp文件后缀名,就知道这个文件中放的是类模板内容

    要用这个类模板的时候直接包含对应的.hpp文件就可以了

     简单来说就是将类模板成员函数的声明和成员函数的实现打包成一个文件,然后这个文件的后缀名规定为.hpp,要用这个类模板的时候直接引入这个.hpp文件就可以了 --- 也是用include + 双引号进行引入

    (大家只要一看到这个.hpp文件后缀名就知道这个文件中装的是一个类模板)


    第八部分 --- 类模板和友元

     1.当全局函数在类内实现的时候,直接在类内将全局函数修饰为友元即可(此时全局函数就能够访问类内的具有私有/保护/公共权限的成员了)

     注意!这个函数虽然是在类内实现的,但是它被friend修饰,会被自动识别为友元全局函数

    2.当全局函数是在类模板外实现的时候,我们该如何在类模板中将这个全局函数声明为友元呢?

    1.在内写出全局函数的声明,并用友元关键字friend来修饰这个声明

     2.在类外进行函数实现 --- 由于参数中用到了泛型参数,所以我们必须将这个全局函数以函数模板的形式来实现,首先是template<模板参数列表>

    然后第一步的函数声明也要从普通函数的函数声明修改为函数模板的声明 ---- 要做的改变就是在函数名后面加上一个 <>

    3.我们要先让编译器知道这个全局函数模板的存在,才能够修饰这个全局函数模板为友元,所函数模板的实现要写在修饰友元之前

    4.由于这个函数模板中又使用了类模板,在使用这个类模板之前我们也要让编译器知道这个类模板的存在,所以我们也要在函数模板的实现之前做一个类模板的声明

     总结:一般无特殊要求都用全局函数的类内实现,然后修饰为友元,第二种方法太麻烦了。

    一旦用到了泛型参数,无论是类还是函数,都必须是模板形态(只有模板才能用泛型参数)

  • 相关阅读:
    Vue入门篇:概念,快速入门,插值表达式,核心特性,基本Vue指令
    二维平面的变换
    ssm保险办理系统毕业设计源码012232
    嵌入式操作Sqlite的8条建议
    gitlab
    Springboot整合阿里云OSS进行上传单个图片,多个图片,删除图片功能
    实战:给docusaurus文档网站配置Algolia 实现全站内容搜索功能-2023.11.16(已解决)
    Kubeadm部署Kubernetes Containerd集群
    【机器学习】六、概率图模型
    SpringBoot | SpringBoot多种过滤器配置方式
  • 原文地址:https://blog.csdn.net/qq_51947882/article/details/126575291