C++_21_模板

news2024/11/15 21:58:11

模板

简介:

一种用于实现通用编程的机制。
通过使用模板我们可以编写可复用的代码,可以适用于多种数据类型。
C++模板的语法使用角括号 < > 来表示泛型类型,并使用关键字 template 来定义和声明模板

概念:

c++范式编程

特点:

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

模板引入:

#include <iostream>
using namespace std;
// 模板的引入
//   想打印任何接收的类型 就得写所有类型的函数进行接收
void print(char a)
{
   cout<<a<<endl;
}
void print(int a)
{
    cout<<a<<endl;
}
void print(double a)
{
    cout<<a <<endl;
}

int main(int argc, char const *argv[])
{
    print('a');
    print(1);
    return 0;
}

模板的使用:

#include <iostream>
using namespace std;
template<class z>
//模板的定义 自己会推导你写入的是什么 将a的类型换成什么
void print(z a)
{
    cout<< a <<endl;
}
int main(int argc, char const *argv[])
{
    print('a');
    print(10);
    print("你好");
    return 0;
}

在这里插入图片描述

模板函数:

语法:

template <class 假设的类型1 ,class 假设的类型2...>
    返回值类型 函数名(形参列表)
{
    	函数体;
}

注意:

当前函数中任何一处使用数据类型的地方都可以使用假设的类型

例子:

在这里插入图片描述

特点:

  • 1 函数模板可以自动推导参数的类型,但是不会进行类型转换
  • 2 函数模板可以自动类型推导,也可以显式指定类型
    • 显式指定类型
      • 函数名<指定的类型1,指定的类型2,…>(实参列表);
  • 3,只能在声明的所在函数中使用

补充:

函数模板会编译两次:

1,在加载时对函数模板进行第一次编译

2,在调用时推导T的类型再次将函数模板编译为模板函数

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

  • 1,函数模板不允许自动类型转化 普通函数能够自动进行类型转
  • 2,函数模板和普通函数同时识别,优先使用普通函数,加<>强制使用函数模板
  • 3,函数模板可以使用<>,普通函数不行

模板函数 局限性:

#include <iostream>
using namespace std;
//                  函数模板的局限性
template <class T>
void method(T t)
{
       cout << t << endl;
}
class A
{
};
int main(int argc, char const *argv[])
{
       method(10);
       A a;
       method(a); // 此时模板可以推导出T的类型为A,但是A类没有重载<<运算符,所以
                  // 无法通过cout输出, 此时语法无错, 但是无法编译生成可执行文件 return 0;
}

解决方案1 : 重载 << 运算符
#include <iostream>
   using namespace std;
template <class T>
void method(T t)
{
       cout << t << endl;
}
class A
{
};
ostream &operator<<(ostream &out, A &a)
{
       out << "打印A的对象" << endl;
       return out;
}
int main(int argc, char const *argv[])
{
       method(10);
       A a;
       method(a);
       return 0;
}
解决方案2 : 指定模版函数
#include <iostream>
               using namespace std;
template <class T>
void method(T t)
{
       cout << t << endl;
}
class A
{
};
// 指定模版函数
template <>
void method<A>(A a)
{
       cout << "打印A的对象" << endl;
}
int main(int argc, char const *argv[])
{
       method(10);
       A a;
       method(a);
       return 0;
}

类模板

概念:

  • 有模板的类

语法:

template<class 假设的类型1,class 假设的类型2....>
{
    
}

作用:

当前类中任何一处使用数据类型的地方都可以使用假设的类型

创建对象

类名 <类型1,类型2...> 对象名(实参列表);
类名 <类型1,类型2...> *对象名 = new类名<类型1,类型2>(实参列表)

模板类作为父类

  • 方案1 子类指明父类模板类型
  • 方案2 子类也是模板类

在这里插入图片描述

模板类的函数声明与实现分离

注意:

  • 每一个 类外实现的函数都是模板函数
 template <class 假设的类型>
   	  返回值类型 类名<><假设的类型>::函数名(形参列表){ }
#include <iostream>
using namespace std;
template <class Q>
class Data
{
private:
       Q q;

public:
       Data();
       Data(Q q);
       Q getQ();
       void setQ(Q q);
};
template <class X>
Data<X>::Data()
{
}
template <class Q>
Data<Q>::Data(Q q)
{
       this->q = q;
}
template <class Q>
Q Data<Q>::getQ()
{
       return q;
}
template <class Q>
void Data<Q>::setQ(Q q)
{
       this->q = q;
}
int main(int argc, char const *argv[])
{
       Data<int> data(10);
       return 0;
}

hpp文件

因为模板类的声明与实现无法分离,故将模板类的声明与实现在同一文件中。该文件的后缀名为 .hpp

示例:

在这里插入图片描述

  • data.hpp
template <class Q>
class Data
{
private:
       Q q;

public:
       Data();
       Data(Q q);
       Q getQ();
       void setQ(Q q);
};
template <class X>
Data<X>::Data()
{
}
template <class Q>
Data<Q>::Data(Q q)
{
       this->q = q;
}
template <class Q>
Q Data<Q>::getQ()
{
       return q;
}
template <class Q>
void Data<Q>::setQ(Q q)
{
       this->q = q;
}

main.cpp

#include <iostream>
#include "data.hpp"
using namespace std;
int main(int argc, char const *argv[])
{
       Data<int> data(10);
       return 0;
}

编译命令

g++ main.cpp

类模板对象作为形参

#include <iostream>
#include "../15_code/data.hpp"
using namespace std;
// 指明类型
// void print(Data<int>& data)
// {
// cout << "xxx" << endl;
// }
// void print(Data<char>& data)
// {
// cout << "YYY" << endl;
// }
// 函数模板
template <class E>
void print(Data<E> &data)
{
    cout << "xxx" << endl;
   }
int main(int argc, char const *argv[])
{
    Data<int> data(10);
    print(data);
    Data<char> data02('A');
    print(data02);
    return 0;
}

自定义集合

作用:

存储一组数据类型相同的数据的容器

特点

可以存储任何一种数据类型 基本类型和自定义类型

#include "array.hpp"
class Person
{
private:
    char *name;

public:
    Person()
    {
        this->name = NULL;
    }
    Person(char *name)
    {
        // 测试存储效果
        cout << name << "被创建" << endl;
        int len = strlen(name);
        this->name = (char *)calloc(len + 1, 1);
        strcpy(this->name, name);
    }
    Person(const Person &pe)
    {
        // if (name != NULL)
        // {
        //     // free(name);  有时候会是野指针 释放野指针就崩了
        //     name = NULL;
        // }
        int len = strlen(pe.name);
        this->name = (char *)calloc(len + 1, 1);
        strcpy(this->name, pe.name);
    }
    // 析构
    ~Person()
    {
        cout << name << "被释放了" << endl;
    } // 不用释放因为  头中已经释放过了

    char *getName()
    {
        return name;
    }
};
int main(int argc, char const *argv[])
{

    // 实验基本数据类型  char sort int float double long bool

    // 实验存 int
    ArrayList<int> nums;
    cout << "实验 int 型" << endl;
    nums.add(1);
    nums.add(2);
    nums.add(3);
    nums.add(4);
    nums.add(5);
    for (int i = 0; i < nums.getlen(); i++)
    {
        cout << nums.get(i) << endl; // 按下标获取
    }

    // 实验存 char 型
    ArrayList<char> cs;
    cs.add('A');
    cs.add('b');
    cs.add('c');
    cs.add('d');
    cs.add(65);
    cout << "实验 char 型" << endl;
    for (int i = 0; i < cs.getlen(); i++)
    {
        cout << cs.get(i) << endl; // 按下标获取
    }

    // 实验long型
    ArrayList<long> lo;
    lo.add(123123123L);
    cout << "实验 long 型" << endl;
    for (int i = 0; i < lo.getlen(); i++)
    {
        cout << lo.get(i) << endl; // 按下标获取
    }

    // 测试bool型
    ArrayList<bool> bol;
    bol.add(true);
    bol.add(false);
    cout << "实验 bool 型" << endl;
    for (int i = 0; i < bol.getlen(); i++)
    {
        cout << bol.get(i) << endl; // 按下标获取
    }

    // 测试string型
    ArrayList<string> str;
    str.add("铁锤打铁");
    str.add("云边有个小卖部");
    cout << "实验 bool 型" << endl;
    for (int i = 0; i < str.getlen(); i++)
    {
        cout << str.get(i) << endl; // 按下标获取
    }


    cout<<"\n"<<endl;
    
    
    //  测试自定义类型
    ArrayList<Person> ps;
    ps.add(Person("哇嘎"));
    ps.add(Person("铁头"));
    ps.add(Person("铁锤"));
    cout << "\n";
    cout << "遍历后的结果" << endl;
    for (int i = 0; i < ps.getlen(); i++)
    {
        cout << ps.get(i).getName() << endl;
    }
    return 0;
}

类型转换

1, C提供的强制转换

语法:(转换后的类型)要转换的数据或变量

2,静态转换

语法:

//基本类型转换 支持
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);

3,动态类型转换

语法:

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);

4,常量转换

语法 : **const_cast **

注意:只能对指针与引用的变量使用

示例

//将非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,重新解释转换

简介

这是最不安全的一种转换机制,最有可能出问题。
主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针

语法

reinterpret_cast<T>

示例

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

总结:

1,强制转换
2,系统提供的函数进行转换
		静态转换
				基本类型 支持
				子转父 支持
				父转子 支持
				不相干 不支持
		动态转换
				基本类型 不支持
				子转父 支持
				父转子 不支持
				不相干 不支持
		常量转换
				常量转非常量
				非常量转常量
		重新解释
				基本类型 不支持
				子转父 支持
				父转子 支持
				不相干 支持

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

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

相关文章

OSPFv3协议几类LSA介绍

OSPFv3协议介绍 与OSPFv2相比&#xff0c;OSPFv3在工作机制上与OSPFv2基本相同&#xff1b;但为了支持IPv6地址格式&#xff0c;OSPFv3对OSPFv2做了一些改动。OSPFv3基于OSPFv2基本原理增强&#xff0c;是一个独立的路由协议&#xff08;v3不兼容v2&#xff09;协议号仍然是89…

java -versionbash:/usr/lib/jvm/jdk1.8.0_162/bin/java:无法执行二进制文件:可执行文件格式错误

实验环境&#xff1a;Apple M1在VMwareFusion使用Utubun Jdk文件错误 &#xfffc; 尝试&#xff1a; 1、重新在网盘下载java1.8 2、在终端通过命令下载 3、确保 JDK 正确安装在系统中&#xff0c;可以通过 echo $JAVA_HOME 检查 JAVA_HOME 环境变量是否设置正确。 &#xfff…

前端JavaScript导出excel,并用excel分析数据,使用SheetJS导出excel

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f…

C语言的文件基础知识

一、文件存在的意义 ① 文件的定义是什么&#xff1f; 文件是以单个名称在计算机上存储的信息集合。文件可以是文本文档、图片、程序等等。文件通常具有三个字母的文件扩展名&#xff0c;用于指示文件类型&#xff08;例如&#xff0c;图片文件常常以 JPEG 格式保存并且文件扩…

【技术编辑与写作】优秀文章整理——如何做个好编辑

【技术编辑与写作】优秀文章整理——如何做个好编辑 本文适用人群&#xff1a; 本文不是严谨教程贴&#xff0c;更多的是好文搜集、经验分享和闲聊&#xff0c;适用于感兴趣想简单了解运营、编辑等行业的人&#xff0c;如果是追求高精尖和专业的&#xff0c;已有N年经验人士请…

【HTTP】方法(method)以及 GET 和 POST 的区别

文章目录 方法&#xff08;method&#xff09;登录上传GET 和 POST 有什么区别&#xff08;面试&#xff09;区别不准确的说法 方法&#xff08;method&#xff09; 首行中的第一部分。首行是由方法、URL 和版本号组成 方法描述了这次请求想干什么&#xff0c;最主要的是&…

无人机之激光避障篇

无人机的激光避障技术是通过激光传感器来感知和避开周围障碍物的一种高级技术。以下是关于无人机激光避障技术的详细解析&#xff1a; 一、技术原理 激光避障技术利用激光束的直线传播和反射特性&#xff0c;通过发送激光束并接收反射回来的信号&#xff0c;来检测和计算周围障…

植物病害识别系统Python+卷积神经网络算法+图像识别+人工智能项目+深度学习项目+计算机课设项目+Django网页界面

一、介绍 植物病害识别系统。本系统使用Python作为主要编程语言&#xff0c;通过收集水稻常见的四种叶片病害图片&#xff08;‘细菌性叶枯病’, ‘稻瘟病’, ‘褐斑病’, ‘稻瘟条纹病毒病’&#xff09;作为后面模型训练用到的数据集。然后使用TensorFlow搭建卷积神经网络算…

提前解锁 Vue 3.5 的新特性

Vue 3.5 是 Vue.js 新发布的版本&#xff0c;虽然没有引入重大变更&#xff0c;但带来了许多实用的增强功能、内部优化和性能改进。 1. 响应式系统优化 Vue 3.5 进一步优化了响应式系统的性能&#xff0c;并且减少内存占用。尤其在处理大型或深度嵌套的响应式数组时&#xff…

2024 新手指南:轻松掌握 Win10 的录屏操作

之前为了节约成本我们公司都采用录制软件操作都方式来为异地的同事进行远程操作培训的。所以我们尝试了不少的录屏工具&#xff0c;这里我就分享下win10怎么录屏的操作过程。 1.福昕录屏大师 链接&#xff1a;www.foxitsoftware.cn/REC/ 这款录屏工具是初学者的理想之选&…

【STL】map和set相关知识详细梳理

1. 预备知识 1.1 关联式容器 在之前&#xff0c;我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、 forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面 存储的是元素本身。…

Git之如何删除Untracked文件(六十八)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

PCL 点云圆柱邻域搜索

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&#xff09; 一、概述 本文将介绍如何使用PCL库进…

L3 逻辑回归

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 在周将使用 LogisticRegression 函数对经典的鸢尾花 (Iris) 数据集进行分类。将详细介绍逻辑回归的数学原理。 1. 逻辑回归的数学原理 逻辑回归是一种线性分…

海外大带宽服务器连接失败怎么办?

在全球化日益加深的今天&#xff0c;海外大带宽服务器已成为企业拓展国际市场、提升业务效率的重要工具。然而&#xff0c;面对复杂多变的网络环境和技术挑战&#xff0c;服务器连接失败的问题时有发生&#xff0c;这不仅影响了企业的正常运营&#xff0c;还可能带来经济损失和…

R语言机器学习算法实战系列(二) SVM算法(Support Vector Machine)

文章目录 介绍原理应用方向下载数据加载R包导入数据数据预处理数据描述数据切割标准化数据设置参数训练模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性保存模型总结系统信息介绍 支持向量机(Support Vector Machine,简称SVM)是一种…

JavaScript发送邮件:实现前端触发的教程?

JavaScript发送邮件的方式&#xff1f;怎么使用JavaScript发信&#xff1f; 无论是用户反馈、联系表单还是自动通知&#xff0c;前端触发的邮件发送功能都能极大地提升用户体验。AokSend将详细介绍如何通过JavaScript发送邮件&#xff0c;实现前端触发的邮件发送功能。 JavaS…

58.【C语言】内存函数(memcpy函数)

目录 1.memcpy *简单使用 翻译: *模拟实现 注意事项: *例题 1.memcpy *简单使用 memcpy:memory copy cplusplus的介绍 点我跳转 翻译: 函数 memcpy void * memcpy ( void * destination, const void * source, size_t num ); 复制内存块 直接从source指向的位置复制num…

SpringBoot开发-数据加密

提示&#xff1a;本篇文章介绍各种加密工具以及数据脱密工具的使用 文章目录 前言一、AES加密算法二、AES加密算法工具使用&#xff08;一&#xff09;引入依赖&#xff08;二&#xff09;编写工具类&#xff08;三&#xff09;测试 三、MD5加密算法四、MD5加密工具使用&#x…

Windows【环境 01】服务器系统重装后的服务恢复(ES\Redis\Jafka\Tomcat)环境变量和服务注册

服务器系统重装后的服务恢复 1.原因2.问题处理2.1 JDK2.2 Elasticsearch2.3 Redis2.4 Jafka2.5 Tomcat 3.总结 1.原因 Windows 服务器系统盘损坏&#xff0c;换了硬盘并重做了系统&#xff0c;其他磁盘未损坏但安装的服务无法正常运行了&#xff0c;包括&#xff1a; Elastic…