06-C++ 模板

模板、类型转换

模板

1. 简介

一种用于实现 通用编程 的机制。

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

通过使用模板,我们可以编写可复用的代码,可以适用于多种数据类型。

c++模板的语法使用尖括号 < > 来表示泛型类型,并使用关键字 template 来定义和声明模板。

示例:

#include <iostream>using namespace std;
// 定义一个模板函数,此处 typename 可以替换成 class 二者作用相同
template<typename T>
T add(T a, T b)
{return a + b;
}int main(int argc, char *argv[])
{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.1 原理

  • 编译器并不是把函数模板处理成能够处理任何类型的函数;

  • 函数模板通过具体类型产生不同的函数

  • 编译器会对函数模板进行 两次编译

    • 声明 的地方对 模板代码本身 进行编译;
    • 调用 的地方对 参数替换后的代码 进行编译

1.2 分类

函数模板

类模板

2. 函数模板

2.1 步骤

  1. 声明
  2. 使用

2.2 声明语法

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

注意:

位置:函数上

2.3 使用语法

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

2.4 注意

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

2.5 示例

#include <iostream>using namespace std;
template<class X>
void add(X a, X b)
{cout << "a+b=" << a + b << endl;
}
void add02(int a, int b)
{cout << "a+b=" << a + b << endl;
}int main(int argc, char *argv[])
{add(10, 20); //a+b=30add(10.2, 20.3); //a+b=30.5add('a', 'b'); //a+b=195//模板可以推导其对应的数据类型,但是无法进行自动转换//add(1, 'a'); // 报错//普通函数可以进行自动类型转换add02(1, 'a'); //a+b=98//模板可以显示替换add<int>(1,2); //a+b=3add<int>(1,'a'); //a+b=3return 0;
}

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

(1)、函数模板不允许自动类型转化,普通函数能够自动进行类型转;

示例:

同上面示例

(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)、函数模板可以使用<>,普通函数不行

2.7 函数模板的局限性

在函数模板的数据类型 为对象的时候,打印对象会报错,因为 没有 重载<< 运算符

解决方案:

  • 重载 << 运算符
  • 函数模板具体化

示例:

#include <iostream>
#include <cstring>
using namespace std;template <class X>
void print(X x)
{cout << x << endl;
}class Person{
//    friend ostream& operator <<(ostream& out,Person& p);friend void print<Person>(Person p);
private:char name[50];int age;
public:Person(){}Person(char *name,int age){strcpy(this->name,name);this->age = age;}
};//1、重载运算符
//ostream& operator <<(ostream& out,Person& p)
//{
//    out << p.name << "\t" << p.age << endl;
//    return out;
//}//2、函数模板具体化
template<> void print<Person>(Person p)
{cout << p.name << "\t" << p.age << endl;
}
int main(int argc, char *argv[])
{print(10);Person p("张三",18);print(p);return 0;
}//10
//张三	18

3. 类模板

3.1 步骤、语法

同函数模板

3.2 位置

在类上

3.3 使用

3.3.1 创建对象

用法:

类名<实际数据类型1,实际数据类型2,...> 对象名(实参列表);

注意:

  • 类模板 实例化对象不能自动推导类型需指定

示例:

#include <iostream>
#include <string>
using namespace std;
template<class X,class Y>
class Data{
private:X x;Y y;
public:Data(){}Data(X x,Y y){this->x = x;this->y = y;}void print(){cout << "x = " << x << endl;cout << "y = " << y << endl;}
};int main(int argc, char *argv[])
{//类模板创建对象Data<int,char> d01(10,'a');d01.print();Data<string,int> d02("张三",18);d02.print();
//    类模板创建对象时必须说明其模板的数据类型
//    Data d03("张三",18); //报错
//    d03.print();return 0;
}//x = 10
//y = a
//x = 张三
//y = 18
3.3.2 类模板的派生
  • 类模板派生 普通子类
  • 类模板派生 类 模板

示例:

#include <iostream>
#include <string>
using namespace std;
template<class X,class Y>
class Data{
private:X x;Y y;
public:Data(){}Data(X x,Y y){this->x = x;this->y = y;}void print(){cout << "x = " << x << endl;cout << "y = " << y << endl;}
};
//子类继承与类模板,要么说明其模板的数据类型
class Content:public Data<int,int>{
public:Content(){}Content(int x,int y):Data(x,y){}
};
//要么自己也是类模板
template <class X,class Z>
class Context:public Data<X,Z>{
public:Context(){}Context(X x,Z y):Data<X,Z>(x,y){}
};
int main(int argc, char *argv[])
{Content c01(10,20);c01.print();Context<string,string> c02("德玛","男");c02.print();return 0;
}//x = 10
//y = 20
//x = 德玛
//y = 男
3.3.3 类模板成员函数外部实现

注意:外部实现函数时也需定义与类相同的函数模板

示例:

#include <iostream>using namespace std;
template<class X,class Y>
class Data{
private:X x;Y y;
public:Data();Data(X x,Y y);void print();
};
//外部实现,也需要定义类的模板
template<class X,class Y>
Data<X,Y>::Data()
{}
template<class X,class Y>
Data<X,Y>::Data(X x,Y y)
{this->x = x;this->y = y;
}
template<class X,class Y>
void Data<X,Y>::print()
{cout << "x = " << x << endl;cout << "y = " << y << endl;
}int main(int argc, char *argv[])
{Data<int,double> d01(10,20.0);d01.print();return 0;
}//x = 10
//y = 20
3.3.4 类模板的分文件实现
在实例化模板之前,编译器对模板的定义体是不处理的
在实例化模板时编译器必须在上下文中可以查看到其定义实体
因此模板的实例化与定义体必须放到同一文件中。
但是如果文件名为.h皆为是头文件,在头文件中定义函数不符合头文件的将声明与实例分开的规则
顾将头文件名改为hpp,并在前内部进行示例化

data.hpp 头文件

#include <iostream>using namespace std;
template<class X,class Y>
class Data{
private:X x;Y y;
public:Data();Data(X x,Y y);void print();
};
template<class X,class Y>
Data<X,Y>::Data()
{}
template<class X,class Y>
Data<X,Y>::Data(X x,Y y)
{this->x = x;this->y = y;
}
template<class X,class Y>
void Data<X,Y>::print()
{cout << "x = " << x << endl;cout << "y = " << y << endl;
}

main.cpp

#include <iostream>
#include "data.hpp"
using namespace std;int main(int argc, char *argv[])
{Data<int,double> d01(10,20.0);d01.print();return 0;
}//x = 10
//y = 20
3.3.5 类模板作为函数参数

注意:该函数必须是类模板的友元函数

  • 类模板的对象作为参数,数据类型写死,就是int,char等,

    • 弊端:在调用该函数时,传递的对象的数据类型就只能是写死的类型
  • 将该函数也创建成与类模板相同的函数模板,函数的参数是 同类模板的数据类型的类模板对象

示例:

#include <iostream>using namespace std;
template<class X,class Y>
class Data{//
//    friend void showData(Data<int,char> & data);template<class Z,class W>friend void showData(Data<Z,W> & data);
private:X x;Y y;
public:Data(){}Data(X x,Y y){this->x = x;this->y = y;}
};//类模板的对象作为参数,数据类型写死,就是int,char,
//弊端:在调用该函数时,传递的对象的数据类型就只能是写死的类型
//void showData(Data<int,char> & data)
//{
//    cout << data.x << "\t" << data.y << endl;
//}//将该函数也创建成与类模板相同的函数模板,
//函数的参数是 同类模板的数据类型的类模板对象
template<class X,class Y>
void showData(Data<X,Y> & data)
{cout << data.x << "\t" << data.y << endl;
}int main(int argc, char *argv[])
{Data<int,char> d01(100,'a');showData(d01); //100    aData<int,double> d02(100,1.0);showData(d02); //100    1return 0;
}

4.练习:自定义集合

示例:数组长度可变,可以存储基本类型数据,也可以存储对象

arraylist.hpp 头文件

#include <iostream>
#include <cstring>
#include <cstdlib>using namespace std;
template<class T>
class ArrayList{
private://记录数组的地址T *data;//记录已存数组的个数int size;//记录可容纳数组的个数int count;
public:ArrayList();~ArrayList();//添加数据函数void add(T t);//获取集合函数T& get(int index);//获取集合长度int getSize();
};
//实现无参构造
template<class T>
ArrayList<T>::ArrayList()
{this->count = 2;this->size = 0;this->data = new T[count];
}
//实现析构
template<class T>
ArrayList<T>::~ArrayList()
{if(data != NULL){delete [] data;data = NULL;}
}
//实现添加数据函数
template<class T>
void ArrayList<T>::add(T t)
{if(size == count){//满了,扩容count = count * 2;T *newData = new T[count];memcpy(newData, data, size*sizeof(T));delete [] data;data = newData;}data[size] = t;size++;
}
//实现获取数据函数
template<class T>
T& ArrayList<T>::get(int index)
{return data[index];
}
//实现获取集合长度函数
template<class T>
int ArrayList<T>::getSize()
{return size;
}

main.cpp

#include <iostream>
#include "arraylist.hpp"using namespace std;void fun01()
{ArrayList<int> a1;a1.add(1);a1.add(3);a1.add(5);a1.add(7);a1.add(9);int size = a1.getSize();cout << "长度为:" << size << endl;for(int i = 0; i < size; i++){cout << a1.get(i) << endl;}
}class Person{
private:char *name;int age;
public:Person(){}Person(char *name, int age){//指针直接赋值this->name = name;//字符串指针不可以使用strcpy,字符串指针存储的是字符串的首地址,使用strcpy会出现内存污染
//        strcpy(this->name, name);this->age = age;}~Person(){}void print_info(){cout << "姓名: " << name << "\t年龄:" << age << endl;}
};void fun02()
{Person p1("张三", 18);Person p2("李四", 13);Person p3("王五", 13);Person p4("钱⑥", 12);
//    p1.print_info();ArrayList<Person> a1;a1.add(p1);a1.add(p2);a1.add(p3);a1.add(p4);int size = a1.getSize();cout << "长度为:" << size << endl;for(int i = 0; i < size; i++){a1.get(i).print_info();}}int main(int argc, char *argv[])
{fun01();fun02();return 0;
}//长度为:5
//1
//3
//5
//7
//9
//长度为:4
//姓名: 张三	年龄:18
//姓名: 李四	年龄:13
//姓名: 王五	年龄:13
//姓名: 钱⑥	年龄:12

类型转换

1.上行转换、下行转换

  • 上行转换

    • 父类指针指向子类空间 安全的
  • 下行转换

    • 子类指针指向父类空间 不安全的

2. 类型转换

2.1 C提供的强制转换
语法: (转换后的类型)要转换的数据或变量  
2.2 静态转换

语法:

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);
2.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);
2.4 常量转换

语法:

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

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

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

相关文章

啊?这也算事务?!

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

Java中的Optional类使用技巧

在Java中&#xff0c;Optional 是一个可以为null的容器对象。如果值存在则isPresent()方法返回true&#xff0c;调用get()方法会返回该对象。 使用Optional可以有效地防止NullPointerException。 下面是一些使用Optional的技巧&#xff1a; 创建Optional对象&#xff1a; Opt…

php-m和phpinfo之间不一致的问题的可能原因和解决办法

1.不同的 PHP配置文件: php -m 和 phpinfo 可能会使用不同的 PHP 配置文件。确保它们都使用相同的配置文件。你可以在命令行中使用 php --ini 来查找当前使用的配置文件位置&#xff0c;并在 phpinfo 中查看 Loaded Configuration File 来确保它们相同。 2.不同的 PHP 版本:确…

uni-app封装表格组件

组件代码&#xff1a; <template><view><uni-table class"tableBox" border stripe emptyText""><!-- 表头行 --><uni-tr class"tableTr"><uni-th :sortable"item.sortable" :class"item.sort…

【人工智能新闻】2023年人工智能热门新闻

欢迎收看我们的特别版时事通讯&#xff0c;重点报道“2023年人工智能热门新闻”今年是人工智能领域的里程碑&#xff0c;展示了重塑技术和我们日常生活的突破性进步和创新。从大型企业投资到革命性的技术发布&#xff0c;2023年的每个月都带来了非凡的成就。 加入我们&#xf…

什么是CAS,什么是AQS,两者到底有什么区别

首发2023-12-29 12:41yuan人生 CAS和AQS在java多线程编程中经常会涉及到&#xff0c;在许多面试的时候也会经常问到。但很多同学却不了解他们&#xff0c;那我们来一起看看什么是CAS&#xff0c;什么是AQS&#xff0c;两者到底有什么区别。 详见&#xff1a;https://www.touti…

用 Python 提取某一个公众号下的所有文章

当我们想要提取某一个公众号下的所有文章时&#xff0c;我们可以借助微信公众平台的开放接口&#xff0c;通过Python编写一个爬虫程序来实现。下面是一个示例代码&#xff0c;以及如何将其转化为一篇详细的微信公众号推文文章。 1. 导入所需库 首先&#xff0c;我们需要导入所…

LLM提示词工程学习_Day01

LLM提示词工程学习_Day01 安装学习环境基础Conda环境安装安装Python安装所需的包Jupyter Notebook 安装获取OpenAI API KEY&#xff0c;并写入工程目录里的.env文件进入Jupyter&#xff0c;先跑一段代码 安装学习环境 基础Conda环境安装 conda环境安装&#xff0c;miniconda也…

序列化机制以及应用场景

当谈到 Java 编程语言时&#xff0c;序列化机制是一个重要而又常见的概念。它允许对象在网络上传输或者保存到持久存储设备中。Java 提供了一个内建的序列化机制&#xff0c;它可以将对象转换为字节流&#xff0c;这个过程称为序列化&#xff0c;同时也能从字节流中重新构建对象…

go-carbon v2.3.1 发布,轻量级、语义化、对开发者友好的 Golang 时间处理库

carbon 是一个轻量级、语义化、对开发者友好的 golang 时间处理库&#xff0c;支持链式调用。 目前已被 awesome-go 收录&#xff0c;如果您觉得不错&#xff0c;请给个 star 吧 github.com/golang-module/carbon gitee.com/golang-module/carbon 安装使用 Golang 版本大于…

AI大模型:无需训练让LLM支持超长输入

显式搜索: 知识库外挂 paper: Unleashing Infinite-Length Input Capacity for Large-scale Language Models with Self-Controlled Memory System 看到最无敌的应用&#xff0c;文本和表格解析超厉害https://chatdoc.com/?viaurlainavpro.com ChatGPT代码实现: GitHub - ar…

【neo4j】desktop下载

【neo4j】desktop下载 https://neo4j.com/download/ 点击download&#xff0c;填写表格 之后就可以正常使用了

设计模式之装饰器模式

装饰器模式 文章目录 装饰器模式定义优缺点优点缺点 示例代码示例代码地址 定义 装饰模式&#xff08;Decorator Pattern&#xff09;是一种比较常见的模式&#xff0c;其定义如下&#xff1a; Attach additional responsibilities to an object dynamically keeping the same…

智慧园区物联综合管理平台感知对象管理能力简述

物联感知对象管理, 不局限于物理传感设备, 还包括物联业务对象, 平台提供标准的设备建模能力以及标准的物联设备、 第三方物联系统SDK接入方案等; 实现对感知对象运行、 报警、 故障状态的反馈以及物联感知对象全生命周期信息管理。 基础定义配置 平台提供物联网目感知对…

Halcon纹理分析texture_laws/trans_from_rgb

Halcon纹理分析 文章目录 Halcon纹理分析1. 纹理滤波器2. 织物折痕检测 纹理是图像表面的一种灰度变化。有的纹理很规则&#xff0c;会以局部小区域为单元重复出现&#xff0c;而有的纹理则呈现出随机性。对于规则的纹理&#xff0c;可以很容易地从中分辨出重复的区域&#xff…

Vue3自定义指令与Composition API实现动态权限控制

Vue3作为新一代前端框架以其强大的性能优化、灵活且可复用的Composition API以及对现代Web开发特性的深入支持&#xff0c;为 一、项目背景与技术选型 本次我们面临的是一项具有高度交互性和丰富业务逻辑的大型项目。考虑到Vue3带来的显著性能提升、更严谨的类型系统&#xf…

最新版 BaseRecyclerViewAdapterHelper4:4.1.2 最简单的QuickViewHolder用法,最简洁的代码,复制可用

为了照顾新手&#xff0c;尽量详细&#xff0c;高手勿喷&#xff01;&#xff01;&#xff01; 怕麻烦的话可以直接下载源码&#xff1a;https://download.csdn.net/download/ERP_LXKUN_JAK/88678044?spm1001.2014.3001.5503 先看文件结构&#xff0c;是不是很简单 AndroidSt…

【Pytorch】学习记录分享10——PyTorchTextCNN用于文本分类处理

【Pytorch】学习记录分享10——PyTorchTextCNN用于文本分类处理 1. TextCNN用于文本分类2. 代码实现 1. TextCNN用于文本分类 具体流程&#xff1a; 2. 代码实现 # coding: UTF-8 import torch import torch.nn as nn import torch.nn.functional as F import numpy as np…

【C++】STL 容器 - set 集合容器 ⑦ ( 查找元素 - set#find 函数 | 获取元素个数 - set#count 函数 )

文章目录 一、查找元素 - set#find 函数1、函数原型 简介2、代码示例 - set#find 函数 二、获取元素个数 - set#count 函数1、函数原型 简介2、代码示例 - set#find 函数 一、查找元素 - set#find 函数 1、函数原型 简介 在 C 语言的 STL 标准模板库 , std::set 集合容器 是一个…

Flash、Ajax各自的优缺点,在使用中如何取舍

Flash 和 Ajax 都是用于 Web 开发的技术&#xff0c;各自有一些优缺点。在使用中取舍需要根据具体的需求和情况来决定。 flash 的优点包括&#xff1a; 动画和多媒体效果丰富&#xff1a;Flash 可以创建非常丰富的动画和多媒体效果&#xff0c;适合制作交互式内容和游戏。跨平…