IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++新经典课程学习笔记之第四章模板与泛型-4.1小节 -> 正文阅读

[C++知识库]C++新经典课程学习笔记之第四章模板与泛型-4.1小节

本小节回顾的知识点分别是模板概念函数模板定义与调用

今天总结的知识分为以下3个点:
(1)概述
(2)函数模板的定义和使用
(3)类型模板参数

(1)概述:

????????所谓的泛型编程:独立于任何特定类型的方式来编写代码。
(a)使用泛型编程时,我们需要提供具体的程序实例所操作的值
(b)模板泛型编程基础,也是创建类or函数的蓝图或公式。创建模板时,我们要给这些蓝图或者公式提供足够多的信息来让这些蓝图或公式真正地转换为具体的类或函数,且这种转变发生在编译时
(c)模板(公式or样板)支持 将 类型作为参数的程序设计方式,从而实现了对泛型程序设计的直接支持。也就是说,C++模板机制允许在定义类、函数时将数据类型作为参数,且模板 分为:函数模板 and 类模板

(2)函数模板的定义和使用:????????

????????声明格式

 template<typename T1,typename T2,...typename Tn>
retName funcName(Params) {
    /*...*/
}

????????解释①模板声明语句:以template关键字开头,后边跟着<>,<>里边的东西叫做模板参数列表or模板实参,若有多个模板参数,则分别用,逗号隔开!且每一个模板参数前都跟着一个template/class关键字,表示该参数是一个模板参数的意思。

template<typename T>
template<typename T1,typename T2>
template<class T1,class T2>
//这种声明模板参数的方式硬记即可!

????????解释②模板参数列表表示的是函数定义中要用到的“类型” or “值”,使用时,用<>把模板实参包起来即可!有时我们必须指定模板参数,有时又不需要,因为编译器可以自己根据一些信息推断出来。

????????解释③编译器在编译阶段时就会根据函数调用的情况推断出对应的模板参数类型T是什么类型

请看以下代码:

template<class T1,class T2>//声明了一个函数模板
void showa(T1 a1, T2 a2) {
    cout << a1 << "\t" << a2 << endl;
}
//不指定模板参数来调用函数模板(也即让编译器自行去推断对应的模板参数的类型)
showa(1, 1.89);//res: 1   1.89
//编译器在编译阶段推断出来这个模板的参数类型为int double之后。
//编译器就会为我们实例化出来一个特定版本的函数
//在这里,这个特定版本的函数就是:
void showa(int a1, double a2) {
    cout << a1 << "\t" << a2 << endl;
}
//指定模板参数来调用函数模板
showa<double,double>(1.28,2.88);//res: 1.28 2.88
//在这里编译器在编译阶段不用经过推断就会给我们实例化出来一个特定版本的函数,即:
void showa(double a1, double a2) {
    cout << a1 << "\t" << a2 << endl;
}

????????补充①:正是因为在编译阶段时,编译器会为我们推断出模板声明语句中的模板类型参数,因此我们不妨直接把函数模板声明为inline内联函数建议编译器在编译时就将内联函数被调用的语句直接替换为该函数的函数体内的all语句,进而提高程序的运行效率!)

比如:

template<class T1,class T2>
inline	//模板函数可以是inline内联的,且inline 的位置放在模板参数列表之后
void showinfo(T1 a1, T2 a2) {
	cout << a1 << "\t" << a2 << endl;
}

????????但是,你注意噢,没有什么inline模板类!

template<class T>
inline    //?错误!根本就没有什么inline内敛模板类之说!
class Person {
public:
	/*...*/
};

????????补充②模板的定义不会导致编译器为你立刻生成该(函数/类)模板的代码只有在我们调用这个函数模板时,编译器才会按照我们给定的参数去实例化对应的特殊版本的函数/类,这个实例化的过程才会生成对应的(函数/类)模板的代码。(说人话:当编译器看到你写的模板代码时,不会produce codes,但当编译器看到你写的调用模板的语句时,就会produce codes!)

????????又因为编译器在生成代码时,需要能够找到模板函数的函数体,so我们usually把模板的定义都放于.h头文件中,要用的时候用#include把模板包含进去你的.cpp文件中即可!

????????比如:

my_Template.h

#ifndef __MY_TEMPLATE_H__
#define __MY_TEMPLATE_H__
#include<iostream>
using namespace std;
template<typename T, int a, int b>
inline	//模板函数可以是inline内联的,且inline 的位置放在模板参数列表之后
int funcadd2(T c) {
	return (int)c + a + b;
}
 
#endif __MY_TEMPLATE_H__

main.cpp

#include<iostream>
#include"my_Template.h"
using namespace std;

int main(void) {
	int result = funcadd2<double,18,12>(10.8);//result = 40
	return 0;
}

????????补充③:最后,再注意一个点:当函数模板的定义位于.h头文件中时,即便是被不同的.cpp源文件重复包含该头文件,也不会产生重定义的问题;而普通函数的定义位于.h头文件中时,若被不同的.cpp源文件重复包含该头文件时,就一定会产生函数重定义的问题!

????????解释④调用函数模板和调用函数的区别不大。当你没有加<>给定该函数一个模板参数T时,则调用函数模板时编译器会根据你调用这个函数模板时给出的实参自动推断模板参数列表中的参数的类型。否则就按照你主动在<>里面提供的类型来do事情!

????????注意

????????函数模板可以不指定<里面的模板参数类型>,但类模板必须指定<里面的模板参数类型>? ? ? ? ? ? ? 且,当我们在使用函数模板时,最好指定对应的<>里面的模板参数类型,否则容易造成如下错误:

template<typename T>
T funcadd(T a1, T a2) {
	return a1 + a2;
}

int res1 = funcadd(1, 1);//res1 = 2
double res2 = funcadd(2.2, 2.2);//res2 = 4.4
//funcadd(1, 10.9);
//?!因为此时编译器就不知道到底是推断为int int 还是 double double了

(3)非模板类型参数:

????????在一般的模板声明语句中,每一个模板参数T前都会右一个typename/class,表示这个T是一个类型参数。此外,我们还可以在模板参数列表<>里边去定义非类型参数

????????非模板类型参数:代表一个数值。

(既然非类型参数代表一个值,我们肯定就不能用typename/class这种关键字来修饰这个值了!当然是用之前学习过的数据类型(int/double/float/selfdefine-type)来指定非模板类型参数了。)

????????当该类or函数模板实例化时,这种非模板类型的参数的值要么由用户自己提供,要么由编译器推断出。但这些非模板类型的参数值必须是常量表达式(右值)。因为模板都是在编译阶段实例化的,而编译阶段不可能运行你的程序,so必须给的是右值!

? ? ? ? 请看以下代码:

//定义一个非模板类型的函数模板
template<int a,int b>
int funcadd2() {
	return a + b;
}
//显式指定非模板类型的参数---在尖括号中提供额外的信息

int result = funcadd2<12,13>();//result = 25
int aa = 9;//变量aa在编译阶段是不可以sure的!只有常量表达式(相当于常数)可以通过编译
int result2 = funcadd2<aa, 13>();//错误!非模板类型的参数必须是右值!

运行结果:?

再比如:

template<typename T,int a,int b>
int funcadd2(T c) {
	return (int)c + a + b;
}	
int result = funcadd2<double,18,12>(10.8);//result = 40

再比如:

template<unsigned L1,unsigned L2>
int charscmp(const char const(&p)[L1], const char(&p2)[L1]) {
	return strcmp(p, p2);
}
int res = charscmp<6, 6>("test1", "test2");// <6, 6>

?当然,你这样乱提供非模板类型的参数是不行的:

int res = charscmp<7, 6>("test1", "test2");
//本来字符数组的长度分别是<6, 6>的,你硬是给它提供成<7,6>,这样肯定是不行的呀!

?????????好,那么以上就是这一的4.1小节我所回顾的内容的学习笔记,希望你也能读懂并且消化完,也希望自己能牢记这些小小的细节知识点,加油吧,我们都在coding的路上~

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-26 08:41:11  更:2021-11-26 08:42:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:28:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码