20 模板

作者: ca8519be679b | 来源:发表于2020-12-28 23:45 被阅读0次

     模板的定义

    模板只是一个框架,不能直接使用,模板的通用性不是万能的,

    使用模板可以方便我们快速设计某一类的问题

    函数的模板

    c++的一种设计思想就是泛型变成,其核心就是模板,c++提供了函数模板和类模板

    1

    如上,使用template<typename T>来表示,我告诉编译器要写模板了,其中,typename可以用class替换(不过一般class常用作类模板,typename用作函数模板,但是都是可以互换的),如果你紧跟着函数,那么就是一个函数的模板

    2

    我们很简单就会写一个交换值的函数,如果我们要替换其他值就要写别的方法,这样就十分重复,函数体基本差不多,就是类型不一样,

    3

    如上,我们给函数加上模板,这里我们不能用swap的原因是std下默认有了swap函数,我们就不要再使用了,会冲突。这里等于是默认上编译器去识别typename为int和double,当然我们也可以调用时指定类型,让编译器明确

    4

    如上,当然我们传入的类型要和指定的对应上。

    函数模板的注意事项

    1自动类型的推导必须一致

    2模板必须确定了类型才能使用

    5

    如上,我们上面就是没有指定类型,结果编译器也无法判断你是要给什么类型使用而报错

    函数模板的案例

    编写方法,实现将char,int都能选择排序的方法,且从大到小排序。

    6

    如上,我们多次使用模板,将函数进行调研,这里不能讲len获取使用到函数中。

    普通函数和模板函数使用的区别

    普通函数调用时可以自动发生隐式转换

    模板函数使用编译器自动推导类型时,不会使用隐式转换,如果指定类型可以发生隐式转换

    7

    如上,我们定义了个int数字相加的方法,我们传入了char字符,也能输出结果,是因为将‘a’转换成int的ascii码值,这就是普通函数的隐式转换

    8

    如上,当我们使用了函数模板,就会发现模板是不会将其转换,而是自动判别为2个不同的类型

    9

    如上,如果我们指定了类型,就会实现强转的效果

    普通函数和函数模板的调用规则

    如果普通函数和模板函数都可以实现,优先使用普通函数

    可以使用空模板参数(不是函数参数列表)列表的模板来强制调用模板

    函数模板也可以发生重载

    如果函数模板可以更好的匹配,优先使用模板

    10

    针对第一项,我们写了2个同名函数,其中一个被模板修饰,我们运行发现是调用的普通函数。

    11

    如上,我们尝试将普通函数的变成声明,编译没有报错,但是运行报错了,提示无法解析外部命令,他没有像我们想象的去调用模板函数,如果我们想使用模板函数,就要调用时加上空模板参数列表

    12

    如上,就是加上调用时类型不指定<>,里面不声明任何类型,可变成模板函数的调用

    13

    如上,我们使用同一个函数名,函数模板的参数列表不一样,也是重载

    14

    如上,我们如果给参数都传入字符型,则可看到调用的是模板,因为没有必要转换,使用模板匹配更方便

    模板的局限性

    通用性模板的不是万能的,比如我们比较大小,可以使用模板比较int,double,字符串等等,但是自定义类型比如Persion就不那么方便了。

    我们这是就可以使用

    15

    如上,我们简单比较数字可以。

    16

    如上,如果我们尝试传入自定义的Person就因为我们没有定义其==方法而运行故障,当然我们就不使用运算符重载了,这里可以使用模板的重载

    17

    如上,我们需要将模板设定为使用引用 ,然后这里使用的指定具体类型的模板重载,将函数返回类型前加template<>,然后里面的参数类型就可以传入Person指定类型了,然后就调用到了我们后定义的模板

    类模板

    类模板定义

    类模板和函数模板一样,就是在类前面加上模板,然后模板类型指代特定的成员类型

    18

    如上,其实也没什么说的,就是指定了不同的类型,然后拿去使用就行了,注意的是这里实例化时要给模板指定类型,并不能自动识别类型。

    类模板和模板函数的区别

    如上张图,1模板并不能自动识别我们的类型,而是需要我们显示指定类型

    2 类模板可以指定默认参数

    19

    如上,我们设置了Agetype可以默认是int,则实例对象时可以只传入string类型给模板

    类模板函数创建时机

    普通成员函数一开始就可以创建,而类模板函数是调用时才创建

    20

    如上,比如我们定义了2个类,仅仅方法名不一样,然后我们做一个测试类,然后定义2个方法去调用,显然目前我们定义的类是不可能同时有2个方法都有的,但是编译没有报错,就是因为模板T类类并不知道我要传入什么,所以函数并没有生成,而是我们指定后才能生成,当然我们运行test就会报错,因为实际传入的类并没有2个方法。

    类模板对象作为函数参数

    一共三种方式

    方式1 将参数类型的模板类对象传给方法

    21

    如上,如果我们test方法参数不给指定模板参数类型会报错,这里我们还是使用引用

    方式2 参数模板化

    22

    如上,我们将方法修改,将参数不写明确类型,用t1,t2替代,但是需要在方法上使用模板,变成模板函数,这样就可以识别对象了

    23

    这里再告诉一个typeid方法,将类传入,可以获得对象然后使用name()方法获得对象名,如上,t1就是string,t2是int

    方式3 将整个类作为模板指定类型传入

    24

    如上,我们就是把Person对象都给传入了,也可以实现调用,这个没啥说的。

    类与模板的继承

    当类继承遇到类模板时需要注意以下几点:

    1当类继承的父类是一个类模板时,子类继承时需要指定父类的类型

    2 如果不指定,无法编译器无法给子类分配内存

    3 如果想灵活的指定类型,子类也必须变成类模板

    25

    如上,我们Base类是一个类模板,然后使用Son去继承他,可以看到提示Base是缺少参数列表的,这是因为Son类目前没法分配空间,虽然想象中时是继承T m,但是这个类型的内存空间不确定,所以就必须继承时指定类型

    26

    如上,就是在父类后面加上<int>上面是给指定了整型,然后就没有报错了。

    27

    如上,我们可以看到指定类型后是可以实例化的。接下来我们看动态指定类型,此时类必须使用类模板

    28

    如上,我们还是用原来的Base类,这里我们声明一个模板类,这里,我们使用模板,就可以实例时再传入类型,这里可以看到2个类型t1,t2,t1是给父类继承指定类型使用的,t2是给自身属性类型使用的

    类模板成员函数类外实现

    29

    再拿模板类来说下最简单的Person的问题,如上是简单的类内定义方法。

    下面把方法注释,先把构造方法给注释,只有声明,类外定义

    30

    如上,如果我们单纯的把类方法考过来不行,因为方法不认识T1,T2,就需要我们加上模板,但是上面的还会报错,就是因为我们没有给Person加上类型

    31

    如上,我们就成功实现了类模板构造方法的类外实现,下面我们类外实现show方法

    32

    你也许会认为方法和模板没有关系,事实上,就是需要指定类型而且还要加template声明

    33

    如上,我们还是要使用模板在方法前面,然后Person后面要加上对应传入的类型。

    类模板分文件编写

    类模板分文件编写有时候会因为使用模板编译,调用时链接不到报错。有两种处理方法,一种是导入cpp文件,一种是cpp和h文件合为hpp文件。

    我们为什么类模板要分文件编写呢,就是因为当我们定义的类多起来以后,都在一个cpp里就显得比较乱,就会一个类定义一个h文件,把类外实现定义到cpp文件中,最后调用cpp作为引用

    34

    如上,我们将Person的基本模板定义添加到Person.h中,这里添加使用vs的项目添加项,里面带的#pragma once表示不重复导入

    35

    我们将类外实现的模板含数,这里包括之前的构造方法和show方法都导入到Person.cpp里。

    36

    我们在原来的demo.cpp里使用main方法,注意,然后运行,会发现出现如上的错误,提示2个外部命令无法解析,这是因为我们的h文件只传入了Person的模板定义,而构造和show方法因为没有指定类型,Person.cpp里的内容没有关联到h文件中,所以实例化和show方法调用都提示无法解析。

    37

     如上,如果我们将cpp导入则关联正确,因为cpp文件是导入了头文件的,同时实现了方法,让编译器在调用时知道找哪里。

    接下来我们将h文件和cpp合并,其实本质是一样的,也能实现

    38

    类模板函数与友元

    我们知道模板函数的类外实现,我们也知道友元,如果属性设置的是private就需要friend修饰来使其能访问成员属性,

    39

    我们如上,定义了个全局函数,注意因为是全局函数,所以想让识别属性,必须使用template模板,但是就是这样我们也报错了,首先,我们需要解决函数问题,因为我们类内使用的是普通函数,虽然参数里有模板,但是全局函数是个模板函数,我们就不能在类内简单声明,这相当于类内friend是个普通函数,如果想让其匹配外部的全局函数,需要加上<>

    40

    我们给声明友元的地方加上<>,可以看到运行报了更多奇怪的错误,别慌,其实这个是因为你方法是在类里声明,而函数定义在类后边,需要把方法移动到类前面,当然,因为方法里又用到了Person类对象,需要我们在方法前写上类的声明,当然还是要加上模板,这就是编译器脑残之处,一级一级告诉他我定义了什么后面有,他才会去找

    41

    类模板案例

    42

    今天我们来自定义数组,数组要求可以存储指定类型,数据存储到堆区,构造方法可以传入数组的容量,提供对应得拷贝构造方法及operator=防止浅拷贝的问题,提供尾插和尾删实现数据增删,使用下标获取数组中对应得元素,获取数组元素个数和数组的容量

    先来写模板类吧

    43

    首选我们只需要指定一个不确定类T,然后这里给数组定义3个属性,容量cap,大小size,T类型指针p,然后构造使用容量传入,就同时开辟堆区空间,析构当然是把堆区空间释放,为了防止野指针,将其职位NULL,记住删除数组要加[],然后还定义了拷贝构造方法,因为拷贝要深拷贝防止浅拷贝问题,我们就将p开辟新的空间,同时将元素覆盖。

    44

    接下来说说删除和插入,对于插入就是维护size位置的对象插入,对于删除,我们逻辑删除即可,使用size--,可以对越界做判断

    45

    对于索引取对象,这里使用,重载运算符[]因为返回的是T类型对象,我们使用index调用,对于=可以使其使用=赋值初始化,如果非空就清空内容,将其深拷贝复制,同时为了能继续=调用,返回自身。

    46

    最后一个show方法,用于显示数组

    47

    然后在test1里测试下,看来是可行的,我们就还差自定义数据类型

    48

    对于Person对象,我们定义有参和默认构造,注意,这里默认参数构造必须要有,否则报错,我们重载<<将对象进行输出。

    49

    如上,我们对自定义类也做一个测试,证明是可用的

    相关文章

      网友评论

          本文标题:20 模板

          本文链接:https://www.haomeiwen.com/subject/qqwtgktx.html