当前位置: 首页 > news >正文

C++ STL学习 之 泛型编程

一,泛型编程

泛型即泛泛,泛即泛指,是一种基于模板机制的一种编程方式。

传统的编程思想中,把软件设计建立在三维空间:数据类型,容器,算法的基础上,针对不同的数据类型,不同的容器对同一的算法设计不同的代码,但这样会生成大量源码,而且代码重用性较低。

而泛型思想把算法与容器、数据类型相分离,使同一算法适用于不同的容器和数据类型,成为通用性算法,这样可以最大限度地节省源代码,实现代码的重用。

二,函数模板

定义:建立一个通用的函数,函数的返回值类型和形参类型不具体指定,而是用一个虚拟的1类型来代表,这个类型叫做泛型。这样可以提高复用性,将类型参数化。

1.定义语法

通过函数前添加一行模板声明,来声明一个函数模板,有两种声明格式:
template<typename T>
template<class T>,此处的T即泛型

2.注意事项

  1)由于是自动类型推导,必须推导出一致的数据类型才能使用,比如传进去一个int ,一个char,就不一致,以交换两个数的函数为例:swap函数里面传入的参数类型必须一致,

template<typename T>
void mySwap(T& a, T& b) {
//这里加 & 用引用传递,就是为了高效又正确地实
// 现两个变量的交换,让函数能真正影响到外部的实参 。T temp = a;a = b;b = temp;
}
void test01() {int n1 = 10;char n2 = 'A';mySwap(n1,n2);//会报错


2)函数的模板必须确定T的类型,如果不能自动推导,就需要手动指定,仍以mySwap(函数)为例:

void test01(){int n3 = 5;int n4 = 8;mySwap<int>(n3, n4);//相当于指定T为int类型。cout << "交换后n3=" << n3 << " n4=" << n4 << endl;
}
3.与普通函数的区别

 1)普通函数调用时可以发生隐式的自动类型转换
2)函数模板在调用时,如果是自动类型推导,不会发生隐式转换,显式指定类型也可以发生隐式类型的转换,如下,显式指定泛型为Int类型后,会发生隐式转换,n4转换为int。

void test01(){int n3 = 5;double n4 = 8;mySwap<int>(n3, n4);//相当于指定T为int类型cout << "交换后n3=" << n3 << " n4=" << n4 << endl;
}

 关于隐式类型的转换,遵循一定的规则:

  • 在算术运算中,低类型可以转换为高类型。
  • 赋值表达式中,赋值运算符右边的类型会转为左边的类型。
  • 函数调用时,实参会转换为形参的类型。
  • 函数返回值时,返回表达式类型会转换为返回值类型。
  • 自定义类型的转换是不支持隐式转换的,因为不安全。需要手动转换,包括了静态转换
 4.调用规则

 1)如果函数模板和普通函数都可以实现, 实现了重载,优先调用普通函数,逻辑是:具体的实现 优先于 通用的实现。
2)可以通过添加空的模板参数列表 <>来强制调用函数模板。
3)函数模也可以直接发生重载。
4)如果函数模板比普通函数可以产生更好的匹配效果,则会优先调用函数模板
案例:

void myPrint(int a, int b) { cout << "a=" << a << "b=" << b << "调用普通函数" << endl; }
template<typename T>
void myPrint(T a,T b) { cout << "a=" << a << "b=" << b << "调用函数模板" << endl; }
template<typename T>
void myPrint(T a, T b,T c) { cout << "a=" << a << "b=" << b << "c=" <<c<< "调用函数模板,三个参数" << endl; }
void test06()
{//1)如果函数模板和普通函数都可以实现,实现了重载,优先调用普通函数。逻辑是:具体的实现高于通用的实现。int a = 10, b = 20;myPrint(a, b);//2)可以通过添加空的模板参数列表<>来强制调用函数模板。myPrint<>(a, b);//3)函数模板之间也可以发生重载//重载就是函数名相同,但参数类型和数量不同,这里是数量不同;int c = 30;myPrint(a, b, c);//4)如果函数模板比普通函数可以产生更好的匹配效果,则会优先调用函数模板//因为普通函数是int类,但函数模板是任意类型,更加适配。char c1 = 'a';char c2 = 'b';myPrint(c1, c2);}

5. 特定模板

template <typename T>
bool myCompare(T a, T b) {cout << "调用函数模板myCompare(T a,T b)" << endl;if (a == b) {//==比较运算符,不支持自定义类型return true;}else{return false;}}
//自定义一个学生类
class Student {public:string m_Name;int m_Age;//构造函数Student(string name,int age): m_Name(name),m_Age(age){}
};
//添加一个特定模板来解决整个不兼容问题
template<> bool myCompare(Student s1, Student s2) 
{cout << "调用特殊模板myCompare(Student s1, Student s2)" << endl;//比较对象类型就是比较,每个对象的属性的值是否相等if (s1.m_Name == s2.m_Name and s1.m_Age == s2.m_Age) {return true;}else {return false;}}void test07() {Student s1("Tom", 12);Student s2("Tom", 12);cout << myCompare(s1, s2);
}

三,类模板

1.类模板的概念

(1) 类模板是比函数模板更加通用的一种方式;类模板就是建立一个更加通用的类,类中的成 员等用到的数据类型都是不仅具体指定,用泛型来表示

 (2)与函数模板的区别:
在使用时,类模板没有自动类型推到的方式,只能是显式指定泛型的类型;

 (3)语法:
类前面加一行模板声明:
template<typename 泛型名称>或者template<class 泛型名称>

举例:

template<class NameType ,class AgeType=int>
class Person {NameType m_Name;// 没有指定具体的类,泛型来表示AgeType m_Age;
public:Person(NameType name, AgeType age){m_Name = name;m_Age = age;}void show() { cout << "name: " << m_Name << ", age: " << m_Age << endl; }void setvalue(NameType name, AgeType age) {m_Name = name;m_Age = age;}};
void test01() {Person<string, int>  p1("szl", 34);Person<string, string> p2("saz", "二十岁");p1.show();p2.show();p1.setvalue("tyz", 23);p1.show();}
2. 与函数模板的结合

根据结合的范围分为以下几种情况
1)指定类模板对象的泛型类型(便于理解,但是灵活度不高,传参的时候必须按照指定类型去传)

2)不指定类模板对象的泛型类型,而是通过函数模板的模板参数类别来指定(提高了灵活性,可以在使用这个函数模板的时候再进行指定)

3)将整个类当成一个泛型,使用函数模板的模板参数列表来指定(灵活性最高,可以传入任意类型的对象)

3. 类模板遇到继承

    如果父类是类模板,会有几种变化:
1)父类是类模板,子类可以是普通类,也可以是类模板

2)父类和子类都是类模板,子类和父类的泛型可以不同,各自指定自己的泛型类型。但是子类在继承父类时需要指定父类的泛型类型。


子类的泛型类型可以在使用的时候再去指定。
3)父类和子类都是类模板,让子类和父类共同使用同一个泛型类型。


总之,可以根据实际需要,在继承一个类模板的时候,直接确定他的泛型类型,或者继续用一个泛型代替,在子类实例化的时候再确定。

练习:

使用模板实现一个模板数组类:TemplateArray,数组中可以存储任意类型的数据,类的属性有:

​    T* data;//数组的地址,数据存储在堆内存
​    int capacity;//数组的容量,即数组中最多可以容纳的元素个数
​    int size=0;//数组中实际元素的个数,初始值为零

并提供以下几个类方法:

从尾部添加数据元素: void addBack(T value);

从尾部删除元素:void delBack();

打印数组: void printArray();

返回数组中元素的个数:int getSize();

返回数组的容量:int getCapacity();

返回指定下标位置的元素:T getValueByIndex(int index);

参考代码:

template<class T>
class TemplateArray
{
private:T* data = NULL;int capacity;int size = 0;
public:TemplateArray(int cap){data = new T[cap];//在对空间申请了对应大小的内存capacity = cap;}~TemplateArray(){if (data!=NULL){delete[]data;data = NULL;}}void addBack(T value){//判断数组是否已满,已满就不能添加了if (capacity==size){cout << "数组已满,无法添加元素" << endl;}else{data[size] = value;size++;}}void delBack(){//先判断有没有元素,有元素才能删除if (size>0){//删除元素其实就是让可访问的有效元素范围进行变化,从尾部删除就是将最后一个元素排除,那么修改size的值即可size--;}}void printArray(){for (size_t i = 0; i < size; i++){cout << data[i] << ",";}cout << endl;}int getSize(){return size;}int getCapacity(){return capacity;}T getValueByIndex(int index){return data[index];}
};
void test04()
{//整型数组TemplateArray<int> tarr(6);tarr.addBack(1);//添加元素tarr.addBack(2);tarr.addBack(3);tarr.addBack(4);tarr.addBack(5);tarr.addBack(6);tarr.addBack(7);//已满,无法添加元素tarr.printArray();//打印元素tarr.delBack();//从尾部删除元素tarr.printArray();cout << "容量:" << tarr.getCapacity() << ",元素个数:" << tarr.getSize() << ",下标为2的元素" << tarr.getValueByIndex(2) << endl;//字符数组TemplateArray<char> tarr_char(3);tarr_char.addBack('a');tarr_char.addBack('b');tarr_char.addBack('c');tarr_char.addBack('d');//已满,无法添加tarr_char.printArray();tarr_char.delBack();tarr_char.printArray();cout << "容量:" << tarr_char.getCapacity() << ",元素个数:" << tarr_char.getSize() << ",下标为0的元素" << tarr_char.getValueByIndex(0) << endl;
}


http://www.dtcms.com/a/330684.html

相关文章:

  • Unity Shader unity文档学习笔记(十九):粘土效果,任意网格转化成一个球(顶点动画,曲面着色器)
  • 算法提升之树上问题-(LCA)
  • vue3使用leaflet地图
  • **超融合架构中的发散创新:探索现代编程语言的挑战与机遇**一、引言随着数字化时代的快速发展,超融合架构已成为IT领域的一种重要趋势
  • 【入门级-算法-2、入门算法:枚举法】
  • 代码随想录Day50:图论(图论理论、深度搜索理论、所有可达路径、广度搜索理论)
  • 表单输入绑定详解
  • 给电脑升级内存,自检太慢,以为出错
  • FPS游戏时,你的电脑都在干什么(CS2)
  • langchain入门笔记03:使用fastapi部署本地大模型后端接口,优化局域网内的问答响应速度
  • 网页加载缓慢系统排查与优化指南
  • 消费级显卡分布式智能体协同:构建高性价比医疗AI互动智能体的理论与实践路径
  • npm介绍,指令合集,换源指令
  • 【大文件上传】分片上传+断点续传+Worker线程计算Hash
  • Bean的生命周期
  • (2-10-1)MyBatis的基础与基本使用
  • 【word】一次选中全部表格,宏方法
  • [工具]vscode 使用AI 优化代码
  • week1-[分支结构]中位数
  • AI技术产品化:核心认知与实战指南
  • 【深度学习计算性能】04:硬件
  • 集成电路学习:什么是Machine Learning机器学习
  • 云原生存储架构设计与性能优化
  • 自动驾驶轨迹规划算法——Apollo OpenSpace Planner
  • 利用GISBox完成超图S3M与OSGB三维模型格式的转换
  • Elasticsearch 中如何配置 RBAC 权限-实现安全的访问控制
  • 现在都是APP,小程序抢购,支持浏览器不支持 SSE
  • GPT-5 提示词优化全攻略:用 Prompt Optimizer 快速迁移与提升,打造更稳更快的智能应用
  • LeetCode——Hot 100【合并区间 最大子数组和】
  • 算法148. 排序链表