C++复习笔记——泛型编程模板

01 模板

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

02 函数模板

C++另一种编程思想称为 泛型编程 ,主要利用的技术就是模板 C++
提供两种模板机制:函数模板和类模板

函数模板语法

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

template<typename T>   <=>  template<class T>
函数声明或定义(同一个函数不能用多个函数模板)
//可以一次建立多个通用数据类型
template<typename T, typename T2>
void Insert_Sort_T(T* array, T2 size)

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

总结:

  1. 函数模板利用关键字 template
  2. 使用函数模板有两种方式:自动类型推导、显示指定类型
int a = 10;
int b = 20;
//利用模板实现交换
//1、自动类型推导
mySwap(a, b);
//2、显示指定类型
mySwap<int>(a, b);
  1. 模板的目的是为了提高复用性,将类型参数化
  2. 使用模板时必须确定出通用数据类型T,并且能够推导出一致的类型
    函数模板在自动推导时不可以进行隐式类型转换
//利用模板提供通用的交换函数
template<class T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
// 1、自动类型推导,必须推导出一致的数据类型T,才可以使用
void test01()
{
int a = 10;
int b = 20;
char c = 'c';
mySwap(a, b); // 正确,可以推导出一致的T
//mySwap(a, c); // 错误,推导不出一致的T类型, 同时表明函数模板不可以进行隐式类型转换
}
// 2、模板必须要确定出T的数据类型,才可以使用
template<class T>
void func()
{
cout << "func 调用" << endl;
}
void test02()
{
//func(); //错误,模板不能独立使用,必须确定出T的类型
func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板
}
int main() {
test01();
test02();
system("pause");
return 0;
}

函数模板与普通函数的区别以及调用规则

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

  1. 普通函数调用时可以发生自动类型转换(隐式类型转换)
  2. 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换;如果利用显示指定类型的方式,可以发生隐式类型转换
//1 普通函数与函数模板区别
template<class T>
T myPlus(T a, T b)
{return a + b;
}
int myPlus2(int a, int b)
{return a + b;
}
void test01()
{int a = 10;int b = 20;char c = 'c'; // a = 97 
//	myPlus(a, c); //类型推导不出来 ,函数模板不可以进行隐式类型转换cout << myPlus<int>(a, c) << endl;//显式指定类型时 ,函数模板可以进行隐式类型转换cout << myPlus2(a, c) <<endl; // 10 + 99  普通函数 可以进行隐式类型转换
}

结果:
在这里插入图片描述

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

  1. 如果出现重载 优先使用普通函数调用,如果普通函数没有实现只声明时仍会优先使用普通函数调用,导致出现错误:无法解析
  2. 如果在普通函数和函数模板出现重载时,想强制调用模板 ,那么可以使用空参数列表
myPrint<>(a, b)
  1. 函数模板之间可以发生重载
template<class T> 
void myPrint(T a ,T b) 
template<class T> 
void myPrint(T a, T b ,T c)
  1. 如果在普通函数和函数模板出现重载时,若函数模板可以产生更好的匹配,那么优先调用函数模板
void myPrint(int a, int  b)
{cout << "普通函数调用 myPrint" << endl;
}
template<class T>
void myPrint(T a ,T b)
{cout << "模板调用的myPrint" << endl;
}

代码:

//2 、普通函数和函数模板的调用规则
template<class T>
void myPrint(T a ,T b)
{cout << "模板调用的myPrint" << endl;
}template<class T>
void myPrint(T a, T b ,T c)
{cout << "模板调用的myPrint(a,b,c)" << endl;
}
void myPrint(int a, int  b)
{cout << "普通函数调用 myPrint" << endl;
}
void test02()
{int a = 10;int b = 20;//1 、如果出现重载  优先使用普通函数调用,如果没有实现,出现错误myPrint(a, b); //myPrint(int a, int  b)只声明未实现时仍会优先使用普通函数调用,导致出现错误:无法解析//2、 如果想强制调用模板 ,那么可以使用空参数列表myPrint<>(a, b);//3、 函数模板可以发生重载int c = 30;myPrint(a, b, c);//4、 如果函数模板可以产生更好的匹配,那么优先调用函数模板char c1 = 'c';char d = 'd';myPrint(c1, d);
}

结果:
在这里插入图片描述

模板的局限性以及解决

局限性
模板的通用性并不是万能的 。对于某些特殊类型(数组、自定义类型)在函数模板内不能使用一些通用操作,导致出错

//数组
void f(T a, T b)
{a = b;
}
void test00() {int a1[1], a2[1];//a1 = a2;f(a1, a2);//未报错,传进去的是指针,避开了数组名是常量的限制
}
//自定义类型
class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};
template<class T>
bool myCompare( T &a , T &b )
{if ( a == b){return true;}return false;
}
test:Person p1("Tom", 10);Person p2("Jerry", 10);int ret2 = myCompare(p1, p2);

结果:==运算符不能操作Person
在这里插入图片描述
解决办法: 具体化
提供模板的重载,可以为这些特定的类型提供具体化的模板,如果具体化能够优先匹配,那么就选择具体化
具体化语法:
template<> 返回值 函数名<具体类型>(参数)

template<> bool myCompare(Person&a, Person &b)

/*通过第三代具体化自定义数据类型,解决上述问题如果具体化能够优先匹配,那么就选择具体化语法  template<> 返回值  函数名<具体类型>(参数)*/ 
template<> bool myCompare<Person>(Person &a, Person &b)
{if ( a.m_Age  == b.m_Age){return true;}return false;
}

总结:

● 利用具体化的模板,可以解决自定义类型的通用化
● 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

03 类模板

类模板语法

//类声明或定义
template<typename T1, typename T2>

解释:

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

类模板与函数模板区别:

  1. 类模板可以有默认类型
    template <class NameType, class AgeType = int> //类模板可以有默认类型
  2. 类模板必须显示指定类型使用,无法自动类型推导
//自动类型推导 ,类模板 不支持//Person p("孙悟空", 100);//显示指定类型Person<string, int> p("孙悟空", 100);

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

● 普通类中的成员函数一开始就可以创建
● 类模板中的成员函数在调用时才创建

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <string>
using namespace std;
//类模板
template <class NameType, class AgeType = int> //类模板可以有默认类型
class Person
{
public:
class Person1
{
public:void showPerson1(){cout << "Person1的调用" << endl;}
};
class Person2
{
public:void showPerson2(){cout << "Person2的调用" << endl;}
};
template<class T>
class myClass
{
public:T obj;void func1(){//类模板中成员函数一开始不会创建出来,而是在运行时才去创建//所以可以通过编译不报错obj.showPerson1();}void func2(){obj.showPerson2();//obj未确定,但可以通过编译不报错}
};
//类模板中成员函数 一开始不会创建出来,而是在运行时才去创建
void test02()
{myClass<Person1>m;m.func1();m.func2();//**可以通过编译不报错,但运行时报错:"showPerson2": 不是 "Person1" 的成员**
}

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

类模板对象做函数参数

类模板实例化出的对象,向函数传参的方式 一共有三种传入方式:

  1. 指定传入的类型 — 直接显示对象的数据类型
void doWork( Person<string ,int> & p ) 
  1. 参数模板化 — 将对象中的参数变为模板进行传递
template<class T1 ,class T2>
void doWork2(Person<T1, T2> & p)
  1. 整个类模板化 — 将这个对象类型 模板化进行传递
template<class T>
void doWork3(T&p)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <string>
using namespace std;//类模板
template <class NameType, class AgeType = int> //类模板可以有默认类型
class Person
{
public:Person(NameType name, AgeType age){this->m_Name = name;this->m_Age = age;}void showPerson(){cout << "姓名:" << this->m_Name << " 年龄: " << this->m_Age << endl;}NameType m_Name;AgeType m_Age;
};
//1  指定传入类型
void doWork( Person<string ,int> & p ) 
{p.showPerson();
}
void test01()
{Person <string, int> p("MT",10);doWork(p);
}
//2 参数模板化
template<class T1 ,class T2>
void doWork2(Person<T1, T2> & p)
{//如何查看类型cout << typeid(T1).name() << endl;cout << typeid(T2).name() << endl;p.showPerson();
}
void test02()
{Person <string, int> p("呆贼", 18);doWork2(p);
}
//3 整体模板化
template<class T>
void doWork3(T&p)
{cout << typeid(T).name() << endl;p.showPerson();
}
void test03()
{Person <string, int> p("劣人", 18);doWork3(p);
}
int main(){
//	test01();test02();test03();system("pause");return EXIT_SUCCESS;
}

类模板与继承

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

  1. 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型。如果不指定,编译器无法给子类分配内存
//class Son:public Base //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> //必须指定一个类型
  1. 如果想灵活指定出父类中T的类型,子类也需变为类模板
    //类模板继承类模板 ,可以用T2指定父类中的T类型
    template<class T1, class T2>
    class Son2 :public Base
    总结:
    如果父类是类模板,子类需要指定出父类中T的数据类型。若想灵活指定父类中T的数据类型,子类也要变成类模板。

类模板实现注意事项

类模板成员函数类外实现
类模板中成员函数类外实现时,需要加上模板参数列表
#include
//类模板中成员函数类外实现

template<class T1, class T2>
class Person {
public:
//成员函数类内声明
Person(T1 name, T2 age);
void showPerson();
public:
T1 m_Name;
T2 m_Age;
};
//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
void test01()
{
Person<string, int> p("Tom", 20);
p.showPerson();
}
int main() {
test01();
system("pause");
return 0;
}

类模板分文件编写

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

#pragma  once 
#include <iostream>
#include <string>
using namespace std;
template<class T1 ,class T2>
class Person
{
public:Person(T1 name,T2 age);void showPerson();T1 m_Name;T2 m_Age;
};
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2  age)
{this->m_Name = name;this->m_Age = age;
}
template <class T1, class T2>
void Person<T1, T2>::showPerson()
{cout << "姓名:" << this->m_Name << "  年龄:  " << this->m_Age << endl;
}

类模板与友元

全局友元函数的实现方式:
● 类内实现
全局函数类内实现 - 直接在类内声明友元即可
● 类外实现
全局函数类外实现 - 需要提前让编译器知道全局函数的存在
类外实现步骤:
1、先做函数模板声明,下方再做函数模板定义,在做友元(函数类模板用到的类模板,则还得先做类声明)

//全局函数配合友元 类外实现 - 先做函数模板声明,下方在做函数模板定义,在做友元 
//函数类模板用到的类模板,则还得先做类声明
template<class T1, class T2> class Person; 
//如果声明了函数模板,可以将实现写到后面,否则需要将实现体写到类的前面让编译器提前看到 
template<class T1, class T2> void printPerson2(Person<T1,T2> & p) 

2、类内声明友元函数是函数模板

//friend void printPerson2(Person<T1, T2>& p);//这是普通函数
friend void printPerson2<>(Person<T1, T2>& p);//要声明这是函数模板,否则无法链接函数模板的实现

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

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

相关文章

透视和仿射变换的区别

仿射变换矩阵通常是2x3的矩阵。 三个特点&#xff1a; 直线依然是直线平行线依然平行 [ x ′ y ′ 1 ] [ a 11 a 12 b 1 a 21 a 22 b 2 0 0 1 ] [ x y 1 ] x ′ a 11 ∗ x a 12 ∗ y b 1 y ′ a 21 ∗ x a 22 ∗ y b 2 \begin{gathered} \begin{bmatrix}x\\y\\1\end{b…

Linux Ubuntu系统安装MySQL并实现公网连接本地数据库【内网穿透】

文章目录 前言1 .安装Docker2. 使用Docker拉取MySQL镜像3. 创建并启动MySQL容器4. 本地连接测试4.1 安装MySQL图形化界面工具4.2 使用MySQL Workbench连接测试 5. 公网远程访问本地MySQL5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定TCP地址远程访问 前言 本文主…

LeetCode每日一题 二叉树的最大深度(二叉树)

题目描述 给定一个二叉树 root &#xff0c;返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 示例 2&#xff1a; 输入&#xff1a;root [1,nul…

【异常处理】Vue报错 Component template should contain exactly one root element.

问题描述 启动VUE项目后控制台报错&#xff1a; Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.翻译为&#xff1a;组件模板应该只包含一个根元素 查看vue代码&#xff0…

spring-jpa

一、介绍 1.1ORM 1.2 Java Persistence API 放在javaee版本 优点 支持持久化复杂的Java对象&#xff0c;简化Java应用的对象持久化开发支持使用JPQL语言进行复杂的数据查询使用简单&#xff0c;支持使用注解定义对象关系表之间的映射规范标准化&#xff0c;由Java官 方统一规…

nmaptocsv.py脚本无法处理结果的备用工具

python nmaptocsv.py -i test.nmap -f ip-fqdn-port-protocol-service-version。 一: 使用背景&#xff1a; 使用一些其他的端口扫描软件&#xff0c;指纹识别可能有些端口 如一次&#xff1a;11011端口是mysql数据库但是别的软件扫不出来是mysql&#xff0c;借助nmap对1101…

火爆全网,软件测试数据库常用 SQL 语句总结,你要的我都有......

前言 直接上干货 数据定义语言(DDL) 主要负责数据库、数据表、视图、键、索引等结构化的操作 常用的语句有&#xff1a;CREATE DATABASE、CREATE TABLE、ALTER TABLE等 字段的常用约束有&#xff1a;PRIMARY KEY、FOREIGN KEY、NOT NULL、UNIQUE、AUTO_INCREMENT、DEFAULT 常…

应用案例 | Softing echocollect e网关助力汽车零部件制造商构建企业数据库,提升生产效率和质量

为了提高生产质量和效率&#xff0c;某知名汽车零部件制造商采用了Softing echocollect e多协议数据采集网关——从机器和设备中获取相关数据&#xff0c;并直接将数据存储在中央SQL数据库系统中用于分析处理&#xff0c;从而实现了持续监控和生产过程的改进。 一 背景 该企业…

Vue开发实例(四)Element-UI部分组件使用方法

Element-UI的使用 一、Icon图标的使用1、用 i 标签使用图标 二、用 el-button 使用图标1、使用type定义样式2、使用plain定义样式3、使用round定义样式4、使用circle定义样式5、带图标和文字的按钮6、按钮禁用7、文字按钮8、按钮组9、加载中 三、Link 文字链接1、基础用法2、禁…

springboot245科研项目验收管理系统

科研项目验收管理系统 摘 要 使用旧方法对科研项目信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在科研项目信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次…

FreeROTS day2

总结DMA空闲中断接收数据的使用方法 首先要要选择串口然后配置串口的参数&#xff0c;配置MDA通道选择接受数据&#xff0c;配置空闲中断&#xff0c;定义一个数据接收的容器&#xff0c;启动MDA传输当串口收到数据时MDA将数据传输到容器中,MDA会一直检测是否有数据当有数据并…

数据结构界的终极幻神----树

目录 一.数的概念和分类 种类 二.重点概念 哈希树: 二叉树的线索化 什么是线索化 为什么要线索化 特殊的查找树 完全二叉树 三.手撕完全二叉树(堆) 重点讲解 向上搜索算法 向下搜索算法 一.数的概念和分类 树&#xff08;tree&#xff09;是包含 n(n≥0) [2] 个节…

使用lnmp环境部署laravel框架需要注意的点

1&#xff0c;上传项目文件后&#xff0c;需要chmod -R 777 storage授予文件权限&#xff0c;不然会报错file_put_contents(/): failed to open stream: Permission denied。 如果后面还是报错没有权限的话&#xff0c;就执行ps -ef |grep php查询php运行用户。然后执行chown …

express基础

express express介绍 官网传送门基于 Node.js 平台&#xff0c;快速、开放、极简的 Web 开发框架express特点 Web 应用 Express 是一个基于 Node.js 平台的极简、灵活的 web 应用开发框架&#xff0c;它提供一系列强大的特性&#xff0c;帮助你创建各种 Web 和移动设备应用。…

Mac 以SH脚本安装Arthas

SH脚本安装Aethas curl -L https://alibaba.github.io/arthas/install.sh | sh安装脚本说明 示例源文件&#xff1a; #! /bin/bash# temp file of as.sh TEMP_ARTHAS_FILE"./as.sh.$$"# target file of as.sh TARGET_ARTHAS_FILE"./as.sh"# update timeo…

什么是端点安全以及如何保护端点

什么是端点安全 端点是指可以接收信号的任何设备&#xff0c;是员工使用的一种计算设备&#xff0c;用于保存公司数据或可以访问 Internet。端点的几个示例包括&#xff1a;服务器、工作站&#xff08;台式机和笔记本电脑&#xff09;、移动设备、虚拟机、平板电脑、物联网、可…

ubuntu下使用MATLAB过程中的若干问题

ubuntu版本&#xff1a;Ubuntu 20.04 内核&#xff1a;Linux 5.15.0-97-generic MATALB版本&#xff1a;MATLAB R2022b 问题1&#xff1a;运行脚本时闪退 报错信息&#xff1a; Inconsistency detected by ld.so: ../elf/dl-tls.c: 517: _dl_allocate_tls_init: Assertion l…

数据指标是什么?为什么要建立指标体系?什么阶段建设?路径是什么?

数据指标是什么&#xff1f;为什么要建立指标体系&#xff1f;什么阶段建设&#xff1f;路径是什么&#xff1f; 一、什么是数据指标体系&#xff1f;二、为什么要建立指标体系&#xff1f;1、统一衡量业务好坏的标准2、指导产品的研发和运营工作3、帮助建设数据分析体系 三、什…

Android 蓝牙开发 入门级(史上最全)

第一节&#xff1a;了解蓝牙 1. 蓝牙基础 蓝牙是一种无线技术标准&#xff0c;用于短距离内的数据交换。 在Android设备上&#xff0c;蓝牙技术允许进行设备发现、配对、连接以及数据传输。 技术始于爱立信公司 1994 方案&#xff0c;它是研究在移动电话和其他配件间进行低功…

Redis面试问题纯享版

基础内容 1、简单介绍以下你了解的Redis 2、对比一下Redis和Memcache的异同&#xff1f; 3、为什么MySQL选用Redis作为缓存&#xff1f; 4、详细聊聊你对Redis各种数据类型的了解&#xff1f; 5、Redis中五种基本数据类型的底层数据结构是什么样的&#xff1f; Redis线程模型…