C++ DAY07 模板

简介

一种用于实现通用编程的机制。
通过使用模板,我们可以编写可复用的代码,可以适用于多种数据类型。
C++ 模板的语法使用角括号 (< >) 来表示泛型类型,并使用关键字 template 来定义和声明模板。
示例
#include <iostream>
// 定义一个模板函数
template<typename T>
T add(T a, T b) {return a + b;
}
int main() {int x = 5, y = 10;float f1 = 2.5, f2 = 3.7;
// 调用模板函数std::cout << "Sum of integers: " << add(x, y) << std::endl;std::cout << "Sum of floats: " << add(f1, f2) << std::endl;return 0;
}

优点:

        提高代码的灵活性和复用性

总结

        将数据类型可以作为参数进行传递

原理      

        编译器会对函数模板进行两次编译,
        在声明的地方对模板代码本身进行编译,
        在调用的地方对参数替换后的 代码进行编译。

分类

函数模板

类模板

函数模板

步骤

        1,声明
        2,使用

声明语法

位置:在函数上

//class 和 typename 都是一样的,用哪个都可以
template<class 模板名,class 模板名,...>
//或
template<typename 模板名,typename 模板名,...>

使用语法

        //在该方法任何一处使用数据类型的地方都可以使用模板名替换

注意

        1,函数模板可以自动推导参数的类型 , 但是不会进行类型转换
        2,函数模板可以自动类型推导 , 也可以显式指定类型
        3,只能在声明的所在函数中使用

示例

//定义模板
template<class T>
//在形参中使用模板
void MySwap(T& a,T& b){
//在局部变量中使用模板T temp = a;a = b;b = temp;
}
void test01(){int a = 10;int b = 20;cout << "a:" << a << " b:" << b << endl;
//1.函数模板可以自动推导参数的类型MySwap(a,b);cout << "a:" << a << " b:" << b << endl;char c1 = 'a';char c2 = 'b';cout << "c1:" << c1 << " c2:" << c2 << endl;
//2. 函数模板显式指定类型MySwap<char>(c1, c2);cout << "c1:" << c1 << " c2:" << c2 << endl;
}

补充

函数模板会编译两次:
        1,在加载时对函数模板进行第一次编译
        2,在调用时推导 T 的类型再次将函数模板编译为模板函数

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

1, 函数模板不允许自动类型转化 普通函数能够自动进行类型转。 ( 示例 1)
template<typename T>
void func(T a, T b){}
//普通函数
void func02(int a, int b){}
void test02()
{
func(10, 20);
//func(10, 'a');//err
func02(10, 'a');
}
2, 函数模板和普通函数同时识别 , 优先使用普通函数 , <> 强制使用函数模板
template<typename T>
void func(T a, T b){
cout<<"函数模板"<<endl;
}
//普通函数
void func(int a, int b){
cout<<"普通函数"<<endl;
}
void test02()
{
//普通函数
func(10, 20);
//显示调用函数模板
func<>(10, 20);
}
3, 函数模板可以使用 <>, 普通函数不行

函数模板的局限性

示例

template <class Z>
void print(Z z)
{cout << z << endl;
}
class Person{
private:char name[32];int age;
public:
Person()
{
}
Person(char name[],int age)
{strcpy(this -> name,name);this -> age = age;
}
~Person()
{
}
};
int main(int argc, char *argv[])
{Person p("张三",18);
//此时函数模板已经推导出类型为Person,但是因为Person没有对<<运算符进行重
载,所以依据无法使用print(p);return 0;
}
解决方案 1 ,重载 << 函数
template <class Z>
void print(Z z)
{cout << z << endl;
}
class Person{
friend ostream& operator <<(ostream& out,Person& p);
private:char name[32];int age;
public:Person(){}Person(char name[],int age){strcpy(this -> name,name);this -> age = age;}~Person(){}
};
ostream& operator <<(ostream& out,Person& p)
{out << p.name << " " << p.age << endl;return out;
}
int main(int argc, char *argv[])
{Person p("张三",18);print(p);return 0;
}
解决方案 2 :函数模板具体化
template <class Z>
void print(Z z)
{cout << z << endl;
}
class Person{
friend void print<Person>(Person p);
private:char name[32];int age;
public:Person(){}Person(char name[],int age){strcpy(this -> name,name);this -> age = age;}~Person(){}
};
//函数模板具体化
template<> void print<Person>(Person p)
{cout << p.name << " " << p.age << endl;
}
int main(int argc, char *argv[])
{Person p("张三",18);print(p);print(10);return 0;
}

类模板

步骤

定义

使用

定义

语法

template <class 模板名1,class 模板名2>

位置:类的上面

使用

创建对象

语法

类名 < 实际数据类型 1, 实际数据类型 2,...> 对象名 ( 实参列表 );
注意
类模板示例化对象是不能自动推导类型 , 需指定
示例
#include <iostream>
#include <cstring>
using namespace std;
//定义模板
template <class T,class Z>
class Data{
private:T t1;Z z1;
public:Data(){}Data(T t,Z z){t1 = t;z1 = z;}void showData(){cout << t1 << " " << z1 << endl;}
};
void test01(){
//创建模板类对象
//Data d01;//类模板示例化对象是不能自动推导类型,错误的
//Data d02(10,'a');//类模板示例化对象是不能自动推导类型,错误的Data<int,char> d03(10,'a');//类模板示例化对象时必须指定模板对应的数据
类型Data<string,float> d04("abc",18.8f);d03.showData();d04.showData();
}
int main(int argc, char *argv[])
{test01();return 0;
}

类模板成员函数外部实现

注意 : 外部实现函数时也需定义与类相同的函数模板
示例
#include <iostream>
#include <cstring>
using namespace std;
//定义模板
template <class T,class Z>
class Data{
private:T t1;Z z1;
public:Data(){}Data(T t,Z z);void showData();
};
template<class T, class Z>
Data<T,Z>::Data(T t, Z z)
{t1 = t;z1 = z;
}
template<class T, class Z>
void Data<T,Z>::showData()
{cout << t1 << " " << z1 << endl;
}
void test01(){Data<string,float> d01("abc",18.8f);d01.showData();
}
int main(int argc, char *argv[])
{test01();return 0;
}

类模板的分文件使用

注意:

        在实例化模板之前,编译器对模板的定义体是不处理的
        在实例化模板时编译器必须在上下文中可以查看到其定义实体
        因此模板的实例化与定义体必须放到同一文件中。
        但是如果文件名为.h 皆为是头文件 , 在头文件中定义函数不符合头文件的将声明与实例分开的规则
        顾将头文件名改为hpp, 并在前内部进行示例化
示例
data.hpp
#ifndef DATA_H
#define DATA_H
#include<iostream>
using namespace std;
template<class T1, class T2>
class Data
{
private:T1 a;T2 b;
public:Data();Data(T1 a, T2 b);void showData();
};
template<class T1, class T2>
Data<T1,T2>::Data()
{
}
template<class T1, class T2>
Data<T1,T2>::Data(T1 a, T2 b)
{this->a = a;this->b = b;
}
template<class T1, class T2>
void Data<T1,T2>::showData()
{cout<<"a="<<a<<", b="<<b<<endl;
}
#endif // DATA_H
main.cpp
#include <iostream>
#include "data.hpp"
using namespace std;
int main(int argc, char *argv[])
{Data<int,char> ob1(100, 'a');ob1.showData();return 0;
}

类模板作为函数参数

注意 : 该函数必须是类模板的友元函数
示例
#include <iostream>
using namespace std;
template <class X>
class A{
friend void printA(A<X>& a);
template <class T>
friend void printB(A<T>& a);
private:X x;
public:A(){}A(X x){this -> x = x;}void showA(){cout << x << endl;}
};
//普通函数作为类模板的友缘函数
void printA(A<int> &a)
{cout << a.x << endl;
}
void test01()
{A<int> a(10);printA(a);
}
//模板函数作为类模板的友缘函数
template <class T>
void printB(A<T> &a)
{cout << a.x << endl;
}
void test02()
{A<int> a(10);printB(a);
}
int main(int argc, char *argv[])
{test02();return 0;
}

类模板的派生

1, 类模板派生普通子类
示例
#include <iostream>
using namespace std;
template <class T>
class Fu{
private:T t;
public:Fu(){}Fu(T t){this -> t = t;}void showFu(){cout << t << endl;}
};
//子类实例父类模板数据
class Zi01:public Fu<int>
{
public:Zi01(){}Zi01(int x):Fu(x){}
};
int main(int argc, char *argv[])
{Zi01 z01(10);z01.showFu();return 0;
}
2, 类模板派生类模板
示例
#include <iostream>
using namespace std;
template <class T>
class Fu{
private:T t;
public:Fu(){}Fu(T t){this -> t = t;}void showFu(){cout << t << endl;}
};
template <class X>
class Zi02:public Fu<X>{
public:Zi02(){}Zi02(X x):Fu<X>(x){}
};
int main(int argc, char *argv[])
{Zi02<char> z02('a');return 0;
}

练习自定义集合

MyArr.hpp
#ifndef MYARR_H
#define MYARR_H
#include <iostream>
using namespace std;
template <class T>
class MyArr{
private:T* ts;int size;//数据长度int count;//容量
public:MyArr();MyArr(const MyArr& myarr);~MyArr();T get(int index);bool add(T t);int getSize();
};
template<class T>
MyArr<T>::MyArr()
{size = 0;count = 5;ts = new T[count];
}
template <class T>
MyArr<T>::MyArr(const MyArr<T>& myarr)
{this->ts = new T[myarr.count];this->size = myarr.size;this->count= myarr.count;/*** C库函数void *memcpy(void *str1, const void *str2, size_t n)* 从存储区 str2 复制 n 个字节到存储区 str1。*/memcpy(ts,myarr.ts,this->size*sizeof(T));
}
template <typename T>
MyArr<T>::~MyArr()
{if(ts == NULL){return;}delete[] ts;ts = NULL;
}
template <typename T>
T MyArr<T>::get(int index)
{if(index < 0 || index > size){return NULL;}return ts[index];
}
template <class T>
bool MyArr<T>::add(T t)
{if(size == count){T * newTs = new T[count * 2];memcpy(newTs,ts,sizeof(T)*size);delete[] ts;ts = newTs;count *= 2;}ts[size] = t;size++;return true;
}
template<class T>
int MyArr<T>::getSize()
{return size;
}
#endif // MYARR_H
main.cpp
#include <iostream>
#include "myarr.hpp"
#include "cstring"
using namespace std;
int main(int argc, char *argv[])
{MyArr<int> arr01;arr01.add(1);arr01.add(2);arr01.add(3);arr01.add(4);arr01.add(5);arr01.add(6);int s01 = arr01.getSize();for(int i = 0; i < s01; i++){cout << arr01.get(i) << endl;}MyArr<int> arr02;arr02.add('a');arr02.add('b');arr02.add('c');arr02.add('d');arr02.add('e');arr02.add('f');int s02 = arr02.getSize();for(int i = 0; i < s02; i++){cout << (char)arr02.get(i) << endl;}MyArr<string> arr03;arr03.add("abc");arr03.add("123");arr03.add("qaz");arr03.add("hello");arr03.add("hi");int s03 = arr03.getSize();for(int i = 0; i < s03; i++){cout << arr03.get(i) << endl;}return 0;
}

类型转换

上行转换

概念 : 父类指针指向子类空间
注意
        安全的

下行转换

子类指针指向父类空间
注意
        不安全

类型转换

静态转换

语法

        static_cast<T>(要转换的数据)

示例

// 基本类型转换 支持
int num = static_cast < int > ( 3.14f );
// 基本指针类型转换 不支持
float f = 0.0f ;
//int *p1 = static_cast<int *>(&f);
// 上行转换 支持(安全)
Base * p2 = static_cast < Base *> ( new Son );
// 下行转换 支持(不安全)
Son * p3 = static_cast < Son *> ( new Base );
// 不相关类型转换 不支持
//Son *p4 = static_cast<Son *>(new Other);

动态转换

语法

        dynamic_cast<T>(要转换的数据 )
示例
// 基本类型转换 不支持
//int num = dynamic_cast<int>(3.14f);
// 基本指针类型转换 不支持
float f = 0.0f ;
//int *p1 = dynamic_cast<int *>(&f);
// 上行转换 支持(安全)
Base * p2 = dynamic_cast < Base *> ( new Son );
// 下行转换 不支持(不安全)
//Son *p3 = dynamic_cast<Son *>(new Base);
// 不相关类型转换 不支持
//Son *p4 = dynamic_cast<Son *>(new Other);
常量转换
语法
        const_cast<T>
注意
        只能对指针与引用的变量使用
示例
//将非const 转换成 const
int num = 10;
const int *p1 = const_cast<const int *>(&num);
//将const 转换成 非const
const int data=0;
int *p = const_cast<int *>(&data);
5, 重新解释转换 (垃圾一个不用看)

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

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

相关文章

IDEA JRebel安装使用教程

1、下载插件 版本列表&#xff1a;https://plugins.jetbrains.com/plugin/4441-jrebel-and-xrebel/versions 下载&#xff1a;JRebel and XRebel 2022.4.1 这里下载2022.4.1版本&#xff0c;因为后续新版本获取凭证会比较麻烦。下载完成会是一个压缩包。 2、安装 选择第一步…

使用VSCode+PlatformIO搭建ESP32开发环境

Arduino IDE本来就是为创客们开发的&#xff0c;虽然没代码提示功能&#xff0c;文件的关系也不清晰&#xff0c;函数不能跳转&#xff0c;头文件也打不开&#xff0c;但人家的初衷就是为了简单而生的&#xff1b;但还是有一些同学喜欢高级点的IDE&#xff0c;也没问题&#xf…

C语言经典好题:字符串左旋(详解)

这题还是比较简单的&#xff0c;各位看完有收获吗 #include<stdio.h> #include<string.h> void leftturn(char arr[],int k) {int len strlen(arr);for (int i 0;i <k;i)//左旋k个字符{//创建临时变量char tmp 0;tmp arr[0];//将数组第一个字符存储到临时变…

【C++进阶之路】第五篇:哈希

文章目录 一、unordered系列关联式容器1.unordered_map&#xff08;1&#xff09;unordered_map的介绍&#xff08;2&#xff09;unordered_map的接口说明 2. unordered_set3.性能对比 二、底层结构1.哈希概念2.哈希冲突3.哈希函数4.哈希冲突解决&#xff08;1&#xff09;闭散…

ArmSoM-RK3588编解码之mpp编码demo解析:mpi_enc_test

一. 简介 [RK3588从入门到精通] 专栏总目录mpi_enc_test 是rockchip官方编码 demo本篇文章进行mpi_enc_test 的代码解析&#xff0c;编码流程解析 二. 环境介绍 硬件环境&#xff1a; ArmSoM-W3 RK3588开发板 软件版本&#xff1a; OS&#xff1a;ArmSoM-W3 Debian11 三. …

【算法】二叉树的存储与遍历模板

二叉树的存储与遍历 const int N 1e6 10;// 二叉树的存储,l数组为左节点,r数组为右结点 int l[N], r[N]; // 存储节点的数据 char w[N]; // 节点的下标指针 int idx 0;// 先序创建 int pre_create(int n) {cin >> w[n];if (w[n] #) return -1;l[n] pre_create(idx)…

nginx配置参数详细解析

文章目录 一、第一级参数&#xff08;全局参数&#xff09;二、第二级参数events使用http使用 三、第三级参数log_format使用server使用error_log使用use使用&#xff08;配置事件驱动模型&#xff09; 四、第四级参数location使用 参考文档 一、第一级参数&#xff08;全局参数…

Python---变量的作用域

变量作用域&#xff1a;指的是变量的作用范围&#xff08;变量在哪里可用&#xff0c;在哪里不可用&#xff09;&#xff0c;主要分为两类&#xff1a;局部变量和全局变量。 定义在函数外部的变量就称之为全局变量&#xff1b; 定义在函数内部的变量就称之为局部变量。 # 定义…

nodejs+vue线上生活超市购物商城系统w2c42

超市管理系统的开发流程包括对超市管理系统的需求分析&#xff0c;软件的设计建模以及编写程序实现系统所需功能这三个阶段。对超市管理系统的需求分析。在这个阶段&#xff0c;通过查阅书籍&#xff0c;走访商场搜集相关资料&#xff0c;了解经营者对软件功能的具体所需和建议…

angular 实现模块共享

angular 实现共享模块 新建共享模块: ng g m material material.module.ts import {NgModule } from @angular/core; import {OverlayModule } from @angular/cdk/overlay; import {CdkTreeModule } f

QML Image、AnimatedImage 加载 Gif动图

文章目录 前言一、Image二、AnimatedImage1. cache false2. cache true&#xff08;默认为true&#xff09; 总结 前言 Image、AnimatedImage 加载 Gif动图&#xff0c;以及AnimatedImage加载Gif是否缓存会导致的问题 一、Image 使用Image加载Gif&#xff0c;显示的只是一张图…

gitlab设置项目clone地址

直接在线修改地址 虽然是个小问题但是我查了很多都是说要去修改配置文件&#xff0c;可是我是docker部署的&#xff0c;修改配置文件之后我还要重新打包镜像想想都不咋规范&#xff0c;后才终于知道可以直接设置&#xff0c;不要改配置文件&#xff01;&#xff01;&#xff0…

PHP中cookie与session使用指南

PHP中cookie与session使用指南 Cookie和session的出现&#xff0c;是为了解决http协议无状态交互的窘境&#xff0c;它们都用于存储客户端的相关信息 0x01 Cookie使用 简介 Cookie 是一种在客户端存储数据的机制&#xff0c;通常用于记录用户的状态和偏好。下面将介绍如何在…

【日常总结】Swagger-ui 导入 showdoc (优雅升级Swagger 2 升至 3.0)

一、场景 环境&#xff1a; 二、存在问题 三、解决方案 四、实战 - Swagger 2 升至 3.0 &#xff08;Open API 3.0&#xff09; Stage 1&#xff1a;引入Maven依赖 Stage 2&#xff1a;Swagger 配置类 Stage 3&#xff1a;访问 Swagger 3.0 Stage 4&#xff1a;获取 js…

docker 安装minio 教程

参考&#xff1a;https://blog.csdn.net/weixin_43888891/article/details/122021704 下载最新版Minio镜像 docker pull minio/minio等同于 docker pull minio/minio:latest检查镜像是否下载 docker images创建文件启动前需要先创建Minio外部挂载的配置文件( /home/minio/c…

Java虚拟机(JVM)的调优技巧和实战

JVM是Java应用程序的运行环境&#xff0c;它负责管理Java应用程序的内存分配、垃圾收集等重要任务。然而&#xff0c;JVM的默认设置并不总是适合所有应用程序&#xff0c;因此需要根据应用程序的需求进行调优。通过对JVM进行调优&#xff0c;可以大大提高Java应用程序的性能和可…

【JUC】十、ForkJoin

文章目录 1、分支合并框架2、案例3、ForkJoinTask4、工作窃取算法5、ForkJoinPool 一个个任务执行在一个个线程上&#xff0c;倘若某一个任务耗时很久&#xff0c;期间其他线程都无事可做&#xff0c;显然没有利用好多核CPU这一计算机资源&#xff0c;因此&#xff0c;出现了&q…

13 redis中的复制的拓扑结构

1、一主一从 为了性能考虑&#xff0c;主节点可以不开启AOF&#xff0c;但是要避免重启。 2、一主多从 适用于读操作的场景。由于从节点多&#xff0c;所以主的复制压力大 3、树状主从 数据先同步到redisB,redisC从节点C,E来看&#xff0c;redisB相当于主机了&#xff0c;可以…

【JavaEE】Servlet实战案例:表白墙网页实现

一、功能展示 输入信息&#xff1a; 点击提交&#xff1a; 二、设计要点 2.1 明确前后端交互接口 &#x1f693;接口一&#xff1a;当用户打开页面的时候需要从服务器加载已经提交过的表白数据 &#x1f693;接口二&#xff1a;当用户新增一个表白的时候&#xff0c;…

玩转系统|长亭雷池WAF详细使用教程——深入了解

目录 配置防护站点 界面操作​ 如何配置域名、端口、上游服务器​ 工作原理​ 在单独设备上部署雷池&#xff08;推荐&#xff09;​ 直接在网站服务器上部署雷池​ 和其他反代设备一起部署的情况​ 配置后网站无法访问&#xff0c;如何排查​ 测试防护效果 确认网站…