C++基础—模版

C++模板是C++语言中实现泛型编程的核心机制,它允许程序员定义通用的代码框架,这些框架在编译时可以根据提供的具体类型参数生成相应的特定类型实例。

泛型编程的特点代码复用和安全性!

模板主要分为两大类:函数模板类模板


函数模板

基本语法:

template <typename T, typename U, ...> 
函数声明或定义

语法解释:

template     ——声明创建模板

typename   ——表明其后面的 符号为一种数据类型,可以用class代替。

      T           ——这个是通用的数据类型,名称可以替换,通常为大写字母。

函数模板的注意事项:

1.模板类型推导(当函数模板被调用时,编译器会根据传递给模板函数的实际参数自动推导出相应的模板参数类型)时同一个通用类型参数要一致

2.函数模板本身并不生成任何代码。只有当模板被实例化,即编译器根据实际使用的参数类型确定出模板参数 T 的具体值时,才会生成对应的具体函数版本

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

a.普通函数调用时可以发生自动类型转换(隐式类型推导)

#include<iostream>
int main()
{int a{ 33 };char b = 'a';std::cout << a+b<< std::endl;return 0;
}//输出结果是
//130

 在a+b时发生了隐式类型转化,'a'转化为ASCN码值结果是33+97所以结果是130

b.函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

#include<iostream>
template<class T>
T add(T a, T b)
{return a + b;
}
int main()
{int a{ 33 };char b = 'a';std::cout <<add(a,b)<< std::endl;return 0;
}//结果出错!

这个会有错误,没有发生隐式转化

c.如果利用显式指定类型的方式,可以发生隐式类型转换

#include<iostream>
template<class T>
T add(T a, T b)
{return a + b;
}
int main()
{int a{ 33 };char b = 'a';std::cout <<add<int>(a,b)<< std::endl;return 0;
}//结果是
//130

 如果将这个代码修改这个,可以正确输出130

函数模版的局限性:

对于函数模板我们需要具体化模板

对于具体化的模板会被优先调用

#include<iostream>
template<class T>
void compare(T a,T b)
{if (a==b){std::cout << "same" << std::endl;}else{std::cout << "Notsame" << std::endl;}
}class first
{
public:first(int a) :number(a) {};int number;
};int main()
{first s(88),ss(888);compare(s, ss);return 0;
}//这个会产生错误

如果具体化模版可以解决这个问题

#include<iostream>
template<class T>
void compare(T& a, T& b)
{if (a == b){std::cout << "same" << std::endl;}else{std::cout << "Notsame" << std::endl;}
}class first
{
public:first(int a) :number(a) {};int number;
};//具体化模板的语法template<>        
void compare(first& a, first& b)
{if (a.number == b.number){std::cout << "same" << std::endl;}else{std::cout << "Notsame" << std::endl;}
}int main()
{first s(88),ss(888);compare(s, ss);return 0;
}//结果是
//Notsame

普通函数和函数模版的调用:

当普通函数与函数模板都能匹配调用时,编译器通常优先选择普通函数。这是因为普通函数更具体,而函数模板需要经过类型推导和实例化的过程。

#include<iostream>void put(int a)
{std::cout << "普通函数的调用" << std::endl;
}template<class T>
void put(T a)
{std::cout << "模板函数的调用" << std::endl;
}int main()
{int a{ 9 };put(a);return 0;
}//结果是
//普通函数的调用

若要强制调用函数模板,可以显式指定模板参数或者空模板参数列表

#include<iostream>void put(int a)
{std::cout << "普通函数的调用" << std::endl;
}template<class T>
void put(T a)
{std::cout << "模板函数的调用" << std::endl;
}int main()
{int a{ 9 };put<int>(a);  //put<>(a);return 0;
}//结果是
//模板函数的调用

函数模板也可以进行重载

#include<iostream>void put(int a)
{std::cout << "普通函数的调用" << std::endl;
}template<class T>
void put(T a)
{std::cout << "模板函数的调用" << std::endl;
}template<class T>
void put(T a,T b)
{std::cout << "重载模板函数的调用" << std::endl;
}int main()
{int a{ 9 };int b{ 9 };put(a,b);return 0;
}//结果是
//重载模板函数的调用

 如果普通函数与模板函数之间模板函数有更好的选择,那会调用模板函数

#include<iostream>void put(int a)
{std::cout << "普通函数的调用" << std::endl;
}template<class T>
void put(T a)
{std::cout << "模板函数的调用" << std::endl;
}int main()
{char a = 'a';put(a);return 0;
}//结果是
//模板函数的调用

类模板

基本语法:

类模板的语法与函数模板一致

template <typename T, typename U, ...> 
函数声明或定义

语法解释:

template     ——声明创建模板

typename   ——表明其后面的 符号为一种数据类型,可以用class代替。

      T           ——这个是通用的数据类型,名称可以替换,通常为大写字母。

类模版和函数模版的区别:

1.类模板没有自动类型推导的使用方式(在C++17后有自动推导的使用方式)

正确的举例:

#include<iostream>
#include<string>template<class T,class Y>
class first
{
public:first(T a,Y b):A(a),B(b){}T A;Y B;
};int main()
{first <int, std::string>secend(44, "haha");std::cout << secend.A << "  " << secend.B;return 0;	
}//输出的结果
44  haha

在C++17之前如果不进行显示类型指示,那会发生错误

举例子:

#include<iostream>
#include<string>
template<class T,class Y>
class first
{
public:first(T a,Y b):A(a),B(b){}T A;Y B;
};int main()
{first secend(44, "haha");std::cout << secend.A << "  " << secend.B;return 0;	
}

2.类模板在模板参数列表中可以有默认参数

#include<iostream>
#include<string>
template<class T,class Y=int>
class first
{
public:first(T a,Y b):A(a),B(b){}T A;Y B;
};int main()
{first <int,std::string>secend(44, "haha");std::cout << secend.A << "  " << secend.B;return 0;	
}

类模板的默认值与函数参数的默认值的很相似,如果进行了显示类型指示那就会覆盖默认值,如果一个参数被设立了默认值,那这个参数右边所有的参数有都需设立为默认。

类模版成员函数的创建时间

1.普通类中的成员函数一开始就可以创建

2.类模板中的成员函数在调用时才创建

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

类模版作为函数参数

1.指定传入的类型——直接显式对象的数据类型

#include <iostream>  template <class T>
class MyClass
{
public:T value;MyClass(T val) : value(val) {}
};void printValue(MyClass<int> &obj)
{std::cout << "Value: " << obj.value << std::endl;
}int main()
{MyClass myIntObject(42);printValue(myIntObject);return 0;
}//输出结果是
Value: 42

2.参数模板化——将对象中的参数变为模板参数进行传递

#include <iostream>  template <class T>  
class MyClass 
{  
public:  T value;  MyClass(T val) : value(val) {}  
};template <class T>  
void printValue(MyClass<T> obj) 
{  std::cout << "Value: " << obj.value << std::endl;  
}  int main() 
{  MyClass<int> myIntObject(42);  MyClass<double> myDoubleObject(3.14);  printValue(myIntObject);  // 自动推导为 MyClass<int>  printValue(myDoubleObject); // 自动推导为 MyClass<double>  return 0;  
}

3.整个类模板化——将这个对象类型,模板化进行传递 

#include <iostream>  template <class T>
class MyClass
{
public:T value;MyClass(T val) : value(val) {}
};template <class T>
void printValue(T obj)
{std::cout << "Value: " << obj.value << std::endl;
}int main()
{MyClass<int> myIntObject(42);MyClass<double> myDoubleObject(3.14);printValue(myIntObject);  // 自动推导为 MyClass<int>  printValue(myDoubleObject); // 自动推导为 MyClass<double>  return 0;
}//输出是
Value: 42
Value: 3.14

类模板成员在类外实现

#include<iostream>
#include<string>
template<class T,class Y>
class first
{
public:first(T a, Y b);void show();T A;Y B;
};//类模板成员函数类外实现
template<class T,class Y>
first<T,Y>::first(T a,Y b)
{A = a;B = b;
}//成员函数类外实现
template<class T,class Y>
void first<T,Y>::show()
{std::cout <<A<<"   " << B;
}int main()
{first <int,std::string>secend(44, "haha");secend.show();return 0;	
}//输出的结果是:
44   haha

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

类模板与继承

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

当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型

如果不想指定,编译器无法给子类分配内存

#include<iostream>
template<class T>
class first
{
public:T a;
};class secend :public first
{
public:};int main()
{class secend AA(33);
}//会出现错误

如果想灵活指定出父类中T的类型,子类也需要变为类模板

#include<iostream>
template<class T>
class first
{
public:T a;
};class secend :public first<int>
{
public:};int main()
{class secend AA(33);
}//不会出错

将子类也定义为模板

#include<iostream>
template<class T>
class first
{
public:T a;
};template<class T,class Y>
class secend :public first<Y>
{
public:T aa;Y b;secend(T aaval, Y bval, Y firstaval): first<Y>(firstaval), aa(aaval), b(bval) {}
};int main()
{class secend <int ,int>AA(33,44,55);
}

类模板与友元

掌握类模板配合友元函数的类内和类外实现

全局函数类内实现,直接在类内声明友元即可、

全局函数类外实现,需要提前让编译器知道全局函数的存在

#include <iostream>  template <typename T>  
class MyClass {  
public:  MyClass(T value) : data(value) {}  // 声明友元函数  friend void printData(const MyClass<T>& obj);  private:  T data;  
};  // 全局函数类内实现(直接在类定义中定义)  
template <typename T>  
void printData(const MyClass<T>& obj) {  std::cout << "Data: " << obj.data << std::endl;  
}  int main() {  MyClass<int> myObject(42);  printData(myObject); // 输出:Data: 42  return 0;  
}
#include <iostream>  // 前置声明模板类  
template <typename T> class MyClass;  // 前置声明全局友元函数  
template <typename T>  
void printData(const MyClass<T>& obj);  template <typename T>  
class MyClass {  
public:  MyClass(T value) : data(value) {}  // 声明友元函数  friend void printData<>(const MyClass<T>&);  private:  T data;  
};  // 全局函数类外实现  
template <typename T>  
void printData(const MyClass<T>& obj) {  std::cout << "Data: " << obj.data << std::endl;  
}  int main() {  MyClass<int> myObject(42);  printData(myObject); // 输出:Data: 42  return 0;  
}

类模板份文件书写

在C++中,类模板通常涉及多个文件的组织:头文件(.h 或 .hpp)用于声明模板类,源文件(.cpp)用于实现模板类的方法。但是,由于类模板的特殊性,其声明和实现通常合并到同一个头文件中。

如果将模板的声明放在头文件中,而实现放在源文件中,当其他源文件通过包含头文件使用模板时,编译器无法看到模板的实现,导致无法完成模板实例化。这与普通类不同,普通类的成员函数实现可以分离在源文件中,因为编译器只需要知道类的接口(声明)即可编译依赖该类的代码。

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

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

相关文章

C++深度解析教程笔记7

C深度解析教程笔记7 第13课 - 进阶面向对象&#xff08;上&#xff09;类和对象小结 第14课 - 进阶面向对象&#xff08;下&#xff09;类之间的基本关系继承组合 类的表示法实验-类的继承 第15课 - 类与封装的概念实验-定义访问级别cmd 实验小结 第16课 - 类的真正形态实验-st…

Web,Sip,Rtsp,Rtmp,WebRtc,专业MCU融屏视频混流会议直播方案分析

随着万物互联&#xff0c;视频会议直播互动深入业务各方面&#xff0c;主流SFU并不适合管理&#xff0c;很多业务需要各种监控终端&#xff0c;互动SIP硬件设备&#xff0c;Web在线业务平台能相互融合&#xff0c;互联互通&#xff0c; 视频混流直播&#xff0c;录存直播推广&a…

vue3+vite项目中,图片显示为src=“[object Object]“

查了半天&#xff0c;网上都是教人改webpack配置&#xff08;很无语……&#xff09; 解决方法&#xff1a; 在原图片&#xff1b;路径后面加上?url // example <img src"/assets/imgs/stop.svg?url" alt"" />

c++ 筛选裁决文书 1985-2021的数据 分析算法的差异

c cpp 并行计算筛选过滤 裁决文书网1985-2021 的300g数据 数据 数据解压以后大概300g&#xff0c;最开始是使用python代码进行计算&#xff0c;但是python实在太慢了&#xff0c;加上多进程也不行&#xff0c; 于是 使用c 进行 计算 c这块最开始使用的是 i7-9700h 用的是单线…

【翻译】Elasticsearch-索引模块

索引块限制对指定的索引的可用的操作类型&#xff08;就是指对该索引能进行什么操作&#xff09;。这些块有不同的风格&#xff0c;可以阻止写、读或元数据操作。块可以通过动态索引设置来设置/移除&#xff0c;也可以通过专用API添加&#xff0c;这也可以确保写入块一旦成功返…

基于Spring Boot的心灵治愈交流平台设计与实现

基于Spring Boot的心灵治愈交流平台设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 系统功能界面图&#xff0c;在系统首页可以查看首页…

餐饮店油烟净化器多久清洗一次?保持厨房健康卫生

我最近分析了餐饮市场的油烟净化器等产品报告&#xff0c;解决了餐饮业厨房油腻的难题&#xff0c;更加方便了在餐饮业和商业场所有需求的小伙伴们。 在餐饮店中&#xff0c;油烟净化器的清洗频率是确保厨房环境清洁的关键之一。那么&#xff0c;油烟净化器多久清洗一次才合适…

Ubuntu下安装并配置SAMBA服务器

今天我要给大家带来一个关于在Ubuntu下安装并配置SAMBA服务器的详细技术博客。不过&#xff0c;在我们开始之前&#xff0c;我得先夸一夸阿贝云免费服务器&#xff0c;这个免费云服务器真是不错的东西啊&#xff01;配置有1核CPU、1G内存、10G硬盘和5M带宽。现在我们开始吧&…

C++类成员函数重载

成员函数重载是指在同一个类里&#xff0c;有两个以上的函数具有相同的函数名。每种实现对应着一个函数体&#xff0c;但是形参的个数或者类型不同。 例如:减法函数重载 创建一个类&#xff0c;在类中定义3个名为subtract的重载成员函数&#xff0c;分别实现两…

【二等奖水平论文】2024五一数学建模C题22页保奖论文+22页matlab和13页python完整建模代码、可视图表+分解结果等(后续会更新)

一定要点击文末的卡片&#xff0c;那是资料获取的入口&#xff01; 点击链接加入群聊【2024五一数学建模】&#xff1a;http://qm.qq.com/cgi-bin/qm/qr?_wv1027&khoTDlhAS5N_Ffp-vucfG5WjeeJFxsWbz&authKey7oCSHS25VqSLauZ2PpiewRQ9D9PklaCxVS5X6i%2BAkDrey992f0t15…

【.Net Core/.Net8教程】(三)如何优雅地校验参数

FluentValidation是一个流行的开源验证库&#xff0c;用于在.NET应用程序中验证输入数据。 它有如下特点 语法简洁强大的验证功能支持多语言和本地化可重用性和模块化易于集成详细的错误消息扩展性高 借助FluentValidation&#xff0c;我们可以达到优雅地校验参数的目的。 环…

能综合验证的RISCV内核开源项目调研选择

1. 评估的背景目的 考虑维度&#xff1a; 资源需求&#xff0c;开放程度&#xff0c;学习难度&#xff0c;工具链资源。 最好是国产FPGA支持&#xff0c;或者开源EDA工具链支持。 目标还是寻求一款在FPGA上低成本跑起来并能够支持一定的程序开发&#xff0c;最好实现一款…

人工智能 | Embedding

Embedding是什么 Embedding是一种将离散的符或对象映射到连续向量空间技术。在自然语言处理领域中&#xff0c;Embedding常用于将单词或句子为向量形式&#xff0c;以便计算机可以更好地理解和处理文本数据。 通过使用Embedding&#xff0c;我们可以将每个单词或句子表示为一…

机器学习常见面试题总结

1、泛华误差的分解 训练模型的目的——最小化损失函数——泛化误差可以分解为偏差&#xff08;Biase&#xff09;、方差&#xff08;Variance&#xff09;和噪声&#xff08;Noise&#xff09;。 bias&#xff1a;拟合值和真实值之间有较大的偏差。用所有可能的训练数据集训练…

eclipse开启服务后,网页无法打开,如何解决?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

kubectl_入门_service详解

Service 我们知道 Pod 的生命周期是有限的。可以用 ReplicaSet 和Deployment 来动态的创建和销毁 Pod&#xff0c;每个 Pod 都有自己的 IP 地址&#xff0c;但是如果 Pod 重建了的话那么他的 IP 很有可能也就变化了。 这就会带来一个问题&#xff1a;比如我们有一些后端的 Po…

jupyter notebook切换conda虚拟环境

首先&#xff0c;切换到某个虚拟环境&#xff0c;本人切换到了d2l环境&#xff1a; (d2l) C:\Users\10129>pip install ipykernel然后&#xff0c;如代码所示安装ipykernel包 最后&#xff0c;按下述代码执行&#xff1a; (d2l) C:\Users\10129>python -m ipykernel i…

用智慧树理解spring原理

记得很小的时候&#xff0c;少儿频道有一款很火的亲子综艺节目叫《智慧树》&#xff0c;里面有一期是这样的情节——两个小孩将沉落在小池塘里面的糖果状的石子拾起放入腰间的收集袋&#xff0c;规定时间内收集数量多者取胜。两个小女孩用不同方法收集&#xff0c;一个每次都弯…

Xcode安装与配置

Xcode安装与配置 Xcode是苹果公司开发的官方集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为macOS和iOS应用程序的开发而设计。它提供了一套完整的开发工具&#xff0c;包括代码编辑器、编译器、调试器和各种图形用户界面&#xff08;GUI&#xff09;设计工具。本文…

mac电脑关于ios端的appium真机自动化测试环境搭建

一、app store 下载xcode,需要登录apple id 再开始下载 二、安装homebrew 1、终端输入命令&#xff1a; curl -fsSL <https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh>如果不能直接安装&#xff0c;而是出现了很多内容&#xff0c;那么这个时候不要着急&…