【C++提高篇】—— C++泛型编程之模板基本语法和使用的详解

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、模板的概念
  • 二、函数模板
    • 2.1 函数模板的使用
    • 2.2 函数模板注意事项
    • 2.3 普通函数与函数模板的区别
    • 2.4 普通函数与函数模板的调用规则
    • 2.5 模板的局限性
  • 三、类模板
    • 3.1 类模板的使用
    • 3.2 类模板与函数模板的区别
    • 3.3 类模板中成员函数的的创建时机
    • 3.4 类模板对象做函数参数
    • 3.5 类模板与继承
    • 3.6 类模板成员函数类外实现
    • 3.7 类模板分文件编写
    • 3.8 类模板与友元


前言

C++中的另一种编程思想称为泛型编程,主要利用模板和标准模板库(STL)。泛型编程通过编写与数据类型无关的代码,提高了代码的复用性和灵活性。STL提供了一套丰富的容器、算法和迭代器,使开发者可以高效地处理各种数据结构和算法。本章节将系统介绍泛型编程的概念、模板的使用方法以及STL的基本组件。

一、模板的概念

模板:模板就是建立通用的模具,大大提高复用性
模板的特点
a. 模板不可以直接使用,它只是一个框架
b. 模板的通用并不是万能的

二、函数模板

2.1 函数模板的使用

语法
template< typename T>
函数声明或定义
解释
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母

示例
传统的实现:对于不同的数据类型的交换函数,要分开每个都要写一遍实现:

//交换整型函数
void swapInt(int &a, int &b){int temp = a;a = b;b = emp;
}
//交换浮点型函数
void swapDouble(double &a, double &b){double temp = a;a = b;b = temp;
}

使用函数模板:对于操作一样的函数,写一次实现即可

template<typename T>
void mySwap(T &a,T &b) {    //传入引用:交换实参T temp = a;a = b;b = temp;
}
//使用
int main()
{int a = 10;  int b = 20;mySwap(a,b);   //利用模板实现交换//1、自动类型推导// mySwap<int>(a,b);  //2、显示指定类型cout<< "a=" << a << "b=" << b <<endl;system("pause");return 0;
}

总结
函数模板定义后,有两种使用方式:
a. 自动类型推导

mySwap(a,b);

b. 显示指定类型

mySwap<int>(a,b);
mySwap<double >(a,b);

2.2 函数模板注意事项

(1) 注意事项一:使用自动类型推导调用的方法
自动类型推导,必须推导出一致的数据类型T, 才可以使用
示例

int main()
{int a = 10;int b = 20;char c = 'c';mySwap(a,b);   //正确,可以推导出一致的T//mySwap(a,c);//错误,a和c数据类型不同,推导不出一致的T类型cout<< "a=" << a << "b=" << b <<endl;system("pause");return 0;
}

(2) 注意事项二
使用模板时必须要确定出T的数据类型,才可以使用
示例

template<classT>
void func(){
cout<< "func调用" <<endl;
}
int main()
{//func();//错误,模板不能独立使用,必须确定出T的类型func<int>();//利用显示指定类型的方式,给T一个类型,才可以使用该模板}system("pause");return 0;
}

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

区别
a. 普通函数调用时可以发生自动类型转换(隐式类型转换)
b. 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
c. 如果利用显示指定类型的方式,可以发生隐式类型转换
示例

void myPrint(int a,int b){cout<< "调用的普通函数a + b:" << a + b <<endl;
}
template<typename T>
void myPrint(T a, T b){cout<< "调用的模板a + b:" << a + b <endl;
}
void test01(){int a = 10;int b = 20;char c = 'c';//myPrint(a,c); //报错,使用自动类型推导时,不会发生隐式类型转换myPrint<int>(a,c);//正确,如果用显示指定类型,可以发生隐式类型转换
}
int main()
{test01();system("pause");return 0;
}

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

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

调用规则如下
1. 如果函数模板和普通函数都可以实现,优先调用普通函数
2. 可以通过空模板参数列表来强制调用函数模板
3. 函数模板也可以发生重载
4. 如果函数模板可以产生更好的匹配,优先调用函数模板
意思就是,例如:当传入的参数为char类型,普通函数形参定义的是int,那么“编译器会觉得隐式转换麻烦“,优先去使用模板函数的实现

示例

void myPrint(int a,int b){cout<< "调用的普通函数" <<endl;
}
template<typename T>
void myPrint(T a, T b){cout<< "调用的模板" <<endl;
}
template<typename T>
void myPrint(Ta, Tb, Tc){     //重载的函数模板cout<< "调用重载的模板" <<endl;
}
void test01(){int a = 10;int b = 20;myPrint(a,b);//优先调用普通函数的实现myPrint<>(a, b);//可以通过空模板参数列表来强制调用函数模板int c = 30;myPrint(a, b, c);//调用重载的函数模板char c2 = 'b';myPrint(c1, c2);//如果函数模板可以产生更好的匹配,优先调用函数模板charc1='a';
}
int main()
{test01();system("pause");return 0;
}

2.5 模板的局限性

局限性:模板的通用性并不是万能的
例如

template<classT>
void func(T a,T b){a=b;}

在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了

template<classT>
void func(T a,T b){if(a>b){...}
}

在上述代码中,如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行
解决
C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板(说白了就是不同的类型,不同的处理,感觉和写普通函数实现差不多了)

三、类模板

3.1 类模板的使用

语法
template< typename T>

template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母

示例

//类模板
template<class T1, class T2>
class Person{
public:Person(NameTypename,AgeTypeage){this->m_Name = name;this->m_Age = age;}void showPerson(){cout<< "name:" << this->m_Name << "age:" << this->m_Age <<endl;}
public:T1 m_Name;T2 m_Age;
};
void test01(){//类模板没有自动类型推导的使用方式,要指定NameType为string类型,AgeType为int类型Person<string,int>P1("孙悟空",999);P1.showPerson();
}
int main()
{test01();system("pause");return 0;
}

注意类模板没有自动类型推导的使用方式,要指定类型

3.2 类模板与函数模板的区别

区别
a. 类模板没有自动类型推导的使用方式,只能用显示指定类型方式
b. 类模板在模板参数列表中可以有默认参数
示例

//有默认参数的类模板
template<class T1,class T2=int>
class Person{
public:Person(T1 name,T2 age){this->mName = name;this->mAge = age;}void showPerson(){cout<< "name:" << this->mName << "age:" << this->mAge <<endl;}
public:T1 mName;T2 mAge;
};
//使用
int main()
{Person<string>p("猪八戒",999);//类模板中的模板参数列表可以指定默认参数//Person<string,int>p("孙悟空",1000);//必须使用显示指定类型的方式,使用类模板p.showPerson();system("pause");return 0;
}

3.3 类模板中成员函数的的创建时机

类模板中成员函数和普通类中成员函数创建时机的区别:
a. 普通类中的成员函数一开始就可以创建
b. 类模板中的成员函数在调用时才创建

示例

总结:类模板中的成员函数并不是一开始就创建的,在调用时才去创建

3.4 类模板对象做函数参数

使用:类模板实例化出的对象,向函数传参的方式
一共有三种传入方式
a. 指定传入的类型 — 直接显示对象的数据类型
b. 参数模板化 — 将对象中的参数变为模板进行传递
c. 整个类模板化 — 将这个对象类型模板化进行传递

示例

总结
1.通过类模板创建的对象,可以有三种方式向函数中进行传参
2.使用比较广泛是第一种:指定传入的类型

3.5 类模板与继承

当类模板碰到继承时,需要注意一下几点
a. 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
b. 如果不指定,编译器无法给子类分配内存
c. 如果想灵活指定出父类中T的类型,子类也需变为类模板

示例

总结:如果父类是类模板,子类需要指定出父类中T的数据类型

3.6 类模板成员函数类外实现

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

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

3.7 类模板分文件编写

问题
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决
解决方式1:直接包含.cpp源文件
解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制

示例

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

3.8 类模板与友元

类模板配合友元函数的类内和类外实现
a. 全局函数类内实现 - 直接在类内声明友元即可
b. 全局函数类外实现 - 需要提前让编译器知道全局函数的存在
示例

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/67662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用JAVA写算法之输入输出篇

本系列适合原来用C语言或其他语言写算法&#xff0c;但是因为找工作或比赛的原因改用JAVA语言写算法的同学。当然也同样适合初学算法&#xff0c;想用JAVA来写算法题的同学。 常规方法&#xff1a;使用Scanner类和System.out 这种方法适用于leetcode&#xff0c;以及一些面试手…

uniapp中h5的微应用解决办法

考虑过用wujie&#xff0c;参考官网Vue组件封装 | 无界的教程&#xff0c;虽然没报错&#xff0c;但是子应用的vue节点根本没挂载上&#xff0c;不知道什么原因&#xff0c;如下图所示 后面采用iframe方式将子应用导入进来&#xff1a; 父应用&#xff1a; <template>&…

数据分析及应用:经营分析中的综合指标解析与应用

目录 1. 市场份额(Market Share) 2. 客户获取成本(Customer Acquisition Cost, CAC) 3. 客户生命周期价值(Customer Lifetime Value, CLV) 4. 客户留存率(Customer Retention Rate, CRR) 5. 净推荐值(Net Promoter Score, NPS) 6. 转化率(Conversion Rate) …

【大数据】Flink + Kafka 实现通用流式数据处理详解

目录 一、前言 二、流式数据处理场景介绍 2.1 流式数据处理概述 2.1.1 流式数据处理场景介绍 2.2 流式数据处理技术栈 2.2.1 数据采集 2.2.2 数据处理 2.2.3 数据存储 2.2.4 数据展示 2.3 流式数据处理场景面临的问题和挑战 三、通用的流式数据处理场景解决方案 3.1…

【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活

&#x1f4ac; 欢迎讨论&#xff1a;如对文章内容有疑问或见解&#xff0c;欢迎在评论区留言&#xff0c;我需要您的帮助&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果这篇文章对您有所帮助&#xff0c;请不吝点赞、收藏或分享&#xff0c;谢谢您的支持&#x…

JAVA-Exploit编写(7)--http-request库文件上传使用续篇

目录 1.http-request简介 2. 依赖导入 3.靶场实战 3.1 简单使用 3.2 增加判断机制 3.3 文件上传成功后的利用 3.4 正则匹配上传文件的返回的路径 1.http-request简介 http-request 是一个库 里面提供很多方法&#xff0c;使得很容易就可以构造http请求,相比于之前使用的…

抖音小程序一键获取手机号

前端代码组件 <button v-if"!isFromOrderList"class"get-phone-btn" open-type"getPhoneNumber"getphonenumber"onGetPhoneNumber">一键获取</button>// 获取手机号回调onGetPhoneNumber(e) {var that this tt.login({f…

NavVis手持激光扫描帮助舍弗勒快速打造“数字孪生”工厂-沪敖3D

在全球拥有近100家工厂的舍弗勒&#xff0c;从2016年开启数字化运营进程&#xff0c;而当前制造、库存、劳动力和物流的数字化&#xff0c;已无法支持其进一步简化工作流程&#xff0c;亟需数字化物理制造环境&#xff0c;打造“数字孪生”工厂。 NavVis为其提供NavVis VLX 3…

2024年博客之星主题创作|Android 开发:前沿技术、跨领域融合与就业技能展望

目录 引言 一、推动 Android 应用创新的核心力量 1.1 人工智能与机器学习的崛起 1.2 增强现实&#xff08;AR&#xff09;与虚拟现实&#xff08;VR&#xff09;的应用扩展 1.3 5G技术的推动 1.4 跨平台开发技术的成熟 1.4.1 React Native 1.4.2 Flutter 1.4.3 Taro …

云原生周刊:K8s 生产环境架构设计及成本分析

开源项目推荐 KubeZoneNet KubeZoneNet 旨在帮助监控和优化 Kubernetes 集群中的跨可用区&#xff08;Cross-Zone&#xff09;网络流量。这个项目提供了一种简便的方式来跟踪和分析 Kubernetes 集群中跨不同可用区的通信&#xff0c;帮助用户优化集群的网络架构、提高资源利用…

[Qt] Box Model | 控件样式 | 实现log_in界面

目录 1、样式属性 &#xff08;1&#xff09;盒模型&#xff08;Box Model&#xff09; 2、控件样式示例 &#xff08;1&#xff09;按钮 &#xff08;2&#xff09;复选框 &#xff08;3&#xff09;单选框 &#xff08;4&#xff09;输入框 &#xff08;5&#xff09…

C++并发编程之提高C++多线程应用可测试性的思想和方法

提高C多线程应用的可测试性是一个重要的课题&#xff0c;因为多线程应用程序通常比单线程应用程序更复杂&#xff0c;更容易出现难以复现的并发问题。为了确保多线程应用的可靠性和正确性&#xff0c;可以采用以下思想和方法来提高其可测试性。 1. 模块化设计 将多线程应用分…

Golang Gin系列-6:Gin 高级路由及URL参数

在本章中&#xff0c;我们将深入研究使用Gin框架的高级路由和URL参数。我们将介绍如何创建和使用路由组、应用中间件、提取路径参数、处理查询字符串、处理静态文件以及使用HTML模板。 路由分组 为什么要使用路由组&#xff1f; 使用路由组有助于保持代码结构整洁有序。当路由…

Javascript IndexedDB(Dexie.js) React 存储大量(5-50M)的完整示例

将 IndexedDB 整合到 React 应用中,可以为你的应用提供高效的客户端存储解决方案,特别适用于需要存储大量结构化数据或离线功能的场景。为了简化 IndexedDB 的使用,通常推荐使用封装好的库,如 Dexie.js 或 idb。这些库提供了更简洁和友好的 API,方便在 React 中进行集成。…

android wifi AsyncChannel(WifiManager和WifiP2pManager)

AynscChannel的讲解 [Android]AsyncChannel介绍-CSDN博客 WifiP2pManager里的channel的使用理解 WifiP2pManager.java public void createGroup(Channel c, ActionListener listener) {checkChannel(c);c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.NETWORK_ID_PE…

【2025】拥抱未来 砥砺前行

2024是怎样的一年 2024在历史画卷上是波澜壮阔的一年&#xff0c;人工智能的浪潮来临&#xff0c;涌现出无数国产大模型。 22年11月ChatGPT发布&#xff0c;它的出现如同在平静湖面上投下一颗巨石&#xff0c;激起了层层波澜&#xff0c;短短五天用户数就达到了100万&#xff0…

FreeRTOS系统移植

前言 学习RTOS之前最重要的就是要学会将系统移植到单片机中&#xff0c;这里可以直接使用cubemx生成移植好的工程&#xff0c;也可以下载库来进行自己移植&#xff0c;这里我选择下载库来自己移植&#xff0c;因为这样可以配合Linux开发stm32单片机程序。 一、下载系统代码 …

算法题目总结-栈和队列

文章目录 1.有效的括号1.答案2.思路 2.最小栈1.答案2.思路 3.前 K 个高频元素1.答案2.思路 4.用栈实现队列1.答案2.思路 5.删除字符串中的所有相邻重复项1.答案2.思路 1.有效的括号 1.答案 package com.sunxiansheng.arithmetic.day10;import java.util.Stack;/*** Descripti…

MySQL 事务及MVCC机制详解

目录 什么是事务 事务的隔离级别 数据库并发的三种场景 读-写 什么是事务 事务就是一组DML语句组成&#xff0c;这些语句在逻辑上存在相关性&#xff0c;这一组DML语句要么全部成功&#xff0c;要么全部失败&#xff0c;是一个整体。MySQL提供一种机制&#xff0c;保证我们…

微服务学习-快速搭建

1. 速通版 1.1. git clone 拉取项目代码&#xff0c;导入 idea 中 git clone icoolkj-microservices-code: 致力于搭建微服务架构平台 1.2. git checkout v1.0.1版本 链接地址&#xff1a;icoolkj-microservices-code 标签 - Gitee.com 2. 项目服务结构 3. 实现重点步骤 …