20-模板




20-模板

模板


 

模板就是建立通用的模具,大大提高复用性

 

注意:

  • 模板不可以直接使用,模板只是一个框架

  • 模板的通用并不是万能的

 

1、函数模板


 

  • C++ 另一种编程思想成为 泛型编程 ,主要利用的技术就是模板

  • C++ 提供两种模板机制:函数模板类模板

     

1.1、函数模板语法


 

函数模板作用:

建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

 

语法:
解释:

template — 声明创建模板

typename — 表示其后面的符号是一种数据类型,typename 可以用 class 代替

T — 通用的数据类型,名称可以替换,通常为大写字母

 

示例:
运行:

 

总结:
  • 函数模板利用关键字 template

  • 使用函数模板有两种方式:自动类型推导、显示指定类型

  • 模板的目的是为了提高复用性,将类型参数化

 

1.2、函数模板注意事项


 

  • 自动类型推导,必须推导出一致的数据类型 T ,才可以使用

  • 模板必须要确定出 T 的数据类型,才可以使用

 

示例:
运行:

 

总结:
  • 使用模板时必须确定出通用数据类型 T,并且能够推导出一致的类型

 

1.3、函数模板案例


 

  • 利用函数模板封装一个排序的函数,可以对 不同数据类型数组 进行排序

  • 排序规则从大到小,排序算法为 选择排序

  • 分别利用 char数组int数组 进行测试

 

示例:
运行:

 

1.4、普通函数与函数模板的区别


 

普通函数与函数模板的区别:

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)

  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

  • 如果利用显示指定类型的方式,可以发生隐式类型转换

 

示例:
运行:

image-20230926184627643

总结:建议使用显式指定类型的方式调用函数模板,因为可以自己确定通用类型 T

 

1.5、普通函数与函数模板的调用规则


调用规则如下

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

  2. 可以通过空模板参数列表来强制调用函数模板

  3. 函数模板也可以发生重载

  4. 如果函数模板可以产生更好的匹配,优先调用函数模板

 

示例:
运行:

总结:既然提供了函数模板,最好不要再提供普通函数,否则容易出现二义性

 

1.6、模板的局限性


 

模板的通用性不是万能的

 

例如

在该示例中,如果用户传入的是数组,因为数组无法直接赋值,所以在该示例中代码无法实现

 

再例如:

如果该示例中,如果 T 的数据类型传入的是 自定义数据类型,同样无法正常运行

 

因此 C++ 为了解决这种问题,提供模板的重载,可以为这些 特定的类型 提供 具体化的模板

 

示例:
运行:

总结:

  • 利用具体化的模板,可以解决自定义类型的通用化

  • 学习模板并不是为了写模板,而是在 STL 能够运用系统提供的模板

 

2、类模板


 

2.1、类模板语法

类模板作用:

  • 建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表

 

语法:
解释:

template — 声明创建模板

typename — 表明其后面的符号是一种数据类型, typename 可以用 class 代替

T — 通用的数据类型,名称可以替换,通常为大写字母

 

示例:
运行:

总结:类模板和函数模板语法相似,在声明模板 template 后面加类,此类称为 类模板

 

2.2、类模板与函数模板区别


 

类模板与函数模板区别主要:

  1. 类模板没有自动类型推导的使用方式

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

 

示例:
运行:

总结:

  • 类模板使用只能用显示指定类型方式

  • 类模板中的模板参数列表可以有默认参数

 

2.3、类模板中成员函数创造时机


 

类模板中成员函数和普通类中成员函数创建时机是有区别的:

  • 普通类中的成员函数一开始就可以创建

  • 类模板中的成员函数在调用时才创建

 

普通类示例:

该代码无法运行,编译器会提示 类 "类名" 没有成员 "函数名"

image-20230928113613761

将报错代码移除

再次运行

 

类模板示例:

该段代码不会报错

运行后会出现错误,因为类模板中的成员函数在调用时才会创建,也就是在调用时才会发现错误

删除相关代码后

 

运行:

 

2.4、类模板对象做函数参数


 

学习目标:

  • 类模板实例化出的对象,向函数传参的方式

 

一共有三种传入方式:

  1. 指定传入的类型 — 直接显示对象的数据类型

  2. 参数模板化 — 将对象中的参数变为模板进行传递

  3. 整个类模板化 — 将这个对象类型 模板化进行传递

 

示例:
运行:

总结:

  • 通过类模板创建的对象,可以有三种方式向函数中进行传参

  • 使用比较广泛的是第一种:指定传入的类型

 

2.5、类模板与继承


 

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

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中 T 的类型

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

  • 如果向灵活制指定出父类中 T 的类型,子类野需变为类模板

 

示例:
运行:

总结:如果父类是类模板,子类需要指定出父类中 T 的数据类型,才能实例化。如果想要灵活指定父类中 T 的数据类型,可以将子类也变为 类模板

 

2.6、类模板成员函数类外实现


 

学习目标:能够掌握类模板中的成员函数类外实现

 

示例:
运行:

总结:类模板中成员函数类外实现时,需要加上模板参数列表

 

2.7、类模板分文件编写


 

学习目标:

  • 掌握类模板成员哈桑农户分文见编写产生的木问题以及解决方式

 

问题:

  • 类模板中成员函数创建时机时在调用阶段,导致分文见编写时链接不到

解决:

  • 解决方案1:直接包含 .cpp 源文件

  • 解决方案2:将声明和实现写到同一个文件中,并更改后缀名为 .hpp ,hpp 时约定的名称,并不是强制

 

报错案例:

main.cpp

 

Person.h

 

Person.cpp

 

运行:

 

解决方案1、直接包含 cpp 源文件:

修改后的 main.cpp

运行:

 

解决方案2、将声明和实现写到同一个文件中,并将文件后缀更改为 hpp

修改后的 main.cpp

 

Person.hpp 文件

 

运行:

 

总结:主流的解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为 .hpp

 

2.8、类模板与友元


 

学习目标:

  • 掌握类模板配合友元函数的类内和类外实现

 

全局函数类内实现 – 直接在类内声明友元即可

全局函数类外实现 – 需要提前让编译器知道全局函数的存在

 

示例:
运行:

总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别

 

类模板案例


 

案例描述:实现一个通用的数组类,要求如下

 

  • 可以对内置数据类型以及自定义数据类型的数据进行存储

  • 将数组中的数据存储到堆区

  • 构造函数中可以传入数组的容量

  • 提供对应的拷贝构造函数以及 operator= 防止浅拷贝问题

  • 提供尾插法和尾减法对数组中的元素进行增加和删除

  • 可以通过下标的方式访问数组中的元素

  • 可以获取数组中当前元素个数和数组的容量

 

示例:

main.cpp

CustomizeArray.hpp

运行:


暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇