浅谈C++|模板篇

一.模板模板概念

模板就是建立通用的模具,大大提高复用性

 模板的特点:

1.模板不可以直接使用,它只是一个框架

2.模板的通用并不是万能的

·C++另一种编程思想称为泛型编程,主要利用的技术就是模板。

·C++提供两种模板机制:函数模板和类模板

 二.函数模板

函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。 

2.1语法

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

自动推导类型代码实例:

#include <iostream>
using namespace std;
template<typename T>
void my_swap(T& a, T& b) {T temp = a;a = b;b = temp;
}int main() {int a = 10,b = 20;char a2 = 'a', b2 = 'b';string a3 = "abc", b3 = "efg";my_swap(a, b);my_swap(a2, b2);my_swap(a3, b3);cout << "a=" << a << " b=" << b << endl;cout << "a2=" << a2 << " b2=" << b2 << endl;cout << "a3=" << a3 << " b3=" << b3 << endl;return 0;
}

 显示指定类型代码:

#include <iostream>
using namespace std;
template<typename T>
void my_swap(T& a, T& b) {T temp = a;a = b;b = temp;
}int main() {int a = 10,b = 20;char a2 = 'a', b2 = 'b';string a3 = "abc", b3 = "efg";my_swap<int>(a, b);my_swap<char>(a2, b2);my_swap<string>(a3, b3);cout << "a=" << a << " b=" << b << endl;cout << "a2=" << a2 << " b2=" << b2 << endl;cout << "a3=" << a3 << " b3=" << b3 << endl;return 0;
}

 2.2注意事项

注意事项:
1.自动类型推导,必须推导出一致的数据类型T,才可以使用

2.模板必须要确定出T的数据类型,才可以使用

 代码实例:

#include <iostream>
using namespace std;
template<typename T>
void my_swap(T a, T B) {T temp = a;a = b;b = a;
}template<typename T>
void fun() {cout << "函数模板调用" << endl;
}int mian() {//fun();//报错,因为不能推理出T的类型int a = 10, b = 20;fun<int>();   //必须这样写,int换为其它类型也可//my_swap(10, 'p');//报错,类型不一致my_swap(20, 90);  my_swap<int>(20, 90);  my_swap(a, b);return 0;
}

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

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

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

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

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

#include <iostream>
using namespace std;
void fun(int one, int two) {cout << "one=" << one << " two=" << two << endl;
}
template<typename T>
void fun1(T one, T two) {cout << "one=" << one << " two=" << two << endl;
}void ce() {int a = 10, b = 20;char c = 'a';fun(a, b);fun(a, c);fun1(a, b);//fun1(a, c);  //报错,因为自动推理类型不会发生隐式转换fun1<int>(a, c);}
int main() {ce();return 0;
}

 总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T

 2.4普通函数和函数模板的调用规则

调用规则如下:
1.如果函数模板和普通函数都可以实现,优先调用普通函数

2.可以通过空模板参数列表来强制调用函数模板
3.函数模板也可以发生重载
4.如果函数模板可以产生更好的匹配,优先调用函数模板

代码实例: 

#include <iostream>
using namespace std;
void fun(int a,int b) {cout << a << ' ' << b << ' ' << endl;cout << "普通函数调用" << endl;
}template<class T>
void fun(T a, T b) {cout << a << ' ' << b << ' ' << endl;cout << "模板函数调用" << endl;
}template<class T>
void fun(T a, T b,T c) {cout << a << ' ' << b << ' ' <<c << endl;cout << "模板函数调用" << endl;
}
int main() {fun(10,20);   //默认调用普通函数fun<>(10,90);  //空列表强制调用模板函数fun<>(10,90,90);  //模板函数可以发生重载return 0;
}

 2.5模板的局限性

 局限性:

模板的通用性并不是万能的

 代码实例:

#include <iostream>
using namespace std;class person {
public:person(string name, int temp) {this->name = name;this->temp = temp;}int temp;string name;
};template <class T>
bool eq(T a, T b) {if (a == b) {return true;}else {return false;}
}int main() {person p1("tom", 20);person p2("tom", 20);//报错,因为自定义的类型,重载‘=’运算符if (eq(p1, p2)) {cout << "=" << endl;}else {cout << "!=" << endl;}return 0;
}

此时模板函数不报错,但运行时会报错。因为类的‘==’没有重载。

 代码实例:

#include <iostream>
using namespace std;
class person {
public:person(string name, int temp) {this->name = name;this->temp = temp;}int temp;string name;
};
template <class T>
bool eq(T a, T b) {if (a == b) {return true;}else {return false;}
}//实例化模板函数
template<> bool eq(person a, person b) {if (a.name == b.name && a.temp == b.temp) {return true;}else {return false;}
}int main() {person p1("tom", 20);person p2("tom", 20);//报错,因为自定义的类型,重载‘=’运算符if (eq(p1, p2)) {cout << "=" << endl;}else {cout << "!=" << endl;}return 0;
}

此时可以运行,函数模板实例化,相当于模板的补充。

三.类模板

类模板作用:
·建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。

类模板不能使用自动推导类型,只能用显示指定的方法。

3.1语法 

template<typename T1, typename T2>
class person {
public:person(T1 name, T2 age) {this->name = name;this->age = age;}T1 name;T2 age;
};

代码实例:

#include <iostream>
using namespace std;
template<typename T1, typename T2>
class person {
public:person(T1 name, T2 age) {this->name = name;this->age = age;}T1 name;T2 age;
};int main() {person<string, int> P("TOM", 90);cout << P.name << ' ' << P.age << endl;return 0;
}

 3.2类模板和函数模板区别

类模板与函数模板区别主要有两点:

1.类模板没有自动类型推导的使用方式

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

代码实例: 

#include <iostream>
using namespace std;
//类模板可以添加默认参数,但是要优先右边全有默认参数yuanze
template<class T1=string, typename T2=int>
class person {
public:person(T1 name, T2 age) {this->name = name;this->age = age;}T1 name;T2 age;
};
int main() {//person P("TOM", 90); //报错因为。类模板没有自动推导型person<string, int> P("TOM", 90);//有默认参数,可以省略类型,但是<>不能省略person<> P1("SOM", 900);cout << P.name << ' ' << P.age << endl;cout << P1.name << ' ' << P.age << endl;return 0;
}

注意:< >不可以省略,设置默认参数时,优先右边。

 3.3类模板中成员函数创建时机

类模板中成员函数和普通类中成员函数创建时机是有区别的:
1.普通类中的成员函数—开始就可以创建
2.类模板中的成员函数在调用时才创建

代码实例:

#include <iostream>
using namespace std;
class person1 {
public:void fun1() {cout << "person1的fun函数调用" << endl;}
};
class person2 {
public:void fun2() {cout << "person2的fun函数调用" << endl;}
};template<typename T>
class p {
public:p(T p) {this->fun = p;}T fun;void fun1() {//定义时不报错,只有实例化后,调用此函数才会检测是否报错。fun.fun1();}void fun2() {//定义时不报错,只有实例化后,调用此函数才会检测是否报错。fun.fun2();}
};int main() {person1 p1;p<person1> p(p1);//此时会正常运行,不会报错,因为没有调用p.fun2();p.fun1();//p.fun2();   运行报错return 0;
}

3.4类模板做函数参数

—共有三种传入方式:
1.指定传入的类型------------直接显示对象的数据类型

⒉参数模板化------------------将对象中的参数变为模板进行传递
3.整个类模板化----------------将这个对象类型模板化进行传递

3.4.1指定传入

代码实例:

#include <iostream>
using namespace std;
//默认参数
template<class T1=string,class T2=int>
class person {
public:T1 name;T2 id;person(T1 name, T2 id) {this->name = name;this->id = id;}void fun() {cout << name << ' ' << id << endl;}
};//此函数不是模板函数,所以其参数要具体的参数
void fun(person<string, int>& p) {p.fun();
}int main() {person<string, int> p("孙悟空", 78);fun(p);return 0;
}

 3.4.2参数模板化

代码实例:

#include <iostream>
using namespace std;
//默认参数
template<class T1 = string, class T2 = int>
class person {
public:T1 name;T2 id;person(T1 name, T2 id) {this->name = name;this->id = id;}void fun() {cout << name << ' ' << id << endl;}
};//实际上是创建模板函数
template<class T1,class T2>
void fun(person<T1, T2>& p) {p.fun();
}int main() {person<string, int> p("猪八戒", 19);fun(p);return 0;
}

3.4.3类模板化

代码实例:

#include <iostream>
#include <string>
using namespace std;
//默认参数
template<class T1 = string, class T2 = int>
class person {
public:T1 name;T2 id;person(T1 name, T2 id) {this->name = name;this->id = id;}void fun() {cout << name << ' ' << id << endl;}
};//将整个类作为一个类型,创建模板函数
template<class T1>
void fun(T1& p) {p.fun();
}int main() {person<string, int> p("沙僧", 19);fun(p);return 0;
}

总结:
通过类模板创建的对象,可以有三种方式向函数中进行传参

使用比较广泛是第一种:指定传入的类型

 3.5类模板与继承

当类模板碰到继承时,需要注意一下几点:
1.当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型,如果不指定,编译器无法给子类分配内存。
2.如果想灵活指定出父类中T的类型,子类也需变为类模板。

 1.代码实例:

#include <iostream>
using namespace std;
template<typename T>
class father {
public:T name;
};//class father :public son 
//如果想灵活指定出父类中T的类型,子类也需变为类模板。
class son :public father<string> {
public:son(string name) {this->name = name;}void fun() {cout << this->name << endl;}
};int main() {son s("Tom");s.fun();return 0;
}

 2.代码实例:

#include <iostream>
using namespace std;
template<typename T>
class father {
public:T name;
};//class father :public son 
//父类是模板类时,子类继承时要指定模板列表
template<class T1,class T2>
class son :public father<T2> {
public:T1 id;son(T1 id,T2 name) {this->name = name;this->id = id;}void fun() {cout << this->name << endl;cout << this->id << endl;}
};int main() {son<int,string> s(100,"Tom");s.fun();return 0;
}

 3.6类模板成员函数类外实现

代码实例:

#include <iostream>
using namespace std;
template<class T1,class T2>
class person {
public:T1 name;T2 id;person(T1 name, T2 id);void fun();};template<class T1, class T2>
person<T1,T2>::person(T1 name, T2 id) {this->name = name;this->id = id;
}template<class T1, class T2>
void person<T1, T2>::fun() {cout << name << endl;cout << id << endl;
}int main() {person<string, int> p("tom", 100);p.fun();return 0;
}

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

3.7类模板分文件编写

问题:

·类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到

解决

·解决方式1:直接包含.cpp源文件
·解决方式2∶将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制

 代码:

person.hpp

#include <iostream>
using namespace std;template<class T1 = string, class T2 = int>
class person {
public:T1 name;T2 age;person(T1 name, T2 age);void person_show();
};template<class T1, class T2>
person<T1, T2>::person(T1 name, T2 age) {this->name = name;this->age = age;
}template<class T1, class T2>
void person<T1, T2>::person_show() {cout << this->name << endl << this->age << endl;
}

person.cpp

#include "person.hpp";
int main() {person<> p("猪八戒", 100);p.person_show();return 0;
}

3.8类模板和友元

全局函数类内实现-直接在类内声明友元即可 
全局函数类外实现·需要提前让编译器知道全局函数的存在 

类内实现 

 代码:

#include <iostream>
using namespace std;
template<class T1=string,class T2=int>
class person {
//定义友元函数,属于全局函数friend void person_show(person<T1,T2> p) {cout << p.name << endl << p.age << endl;}
public:person(T1 name, T2 age);
private:T1 name;T2 age;
};template<class T1, class T2>
person<T1,T2>::person(T1 name, T2 age) {this->name = name;this->age = age;
}int main() {person<> p("小明", 100);person_show(p);return 0;
}

 

 类外实现

代码:

#include <iostream>
using namespace std;//先声明有person类,防止报错
template<class T1, class T2>
class person;//写在person定义之前,防止找不到报错
template<class T1, class T2>
void person_show(person<T1, T2> &p) {cout << p.name << endl << p.age << endl;
}template<class T1,class T2>
class person {//friend void person_show(person<T1, T2>& p);  //错误写法,因为person_show是模板函数,此时T1是模板没的T1friend void person_show<>(person<T1, T2>& p);//正确写法,加入模板列表声明是模板
public:person(T1 name, T2 age);
private:T1 name;T2 age;
};template<class T1, class T2>
person<T1,T2>::person(T1 name, T2 age) {this->name = name;this->age = age;
}int main() {person<string, int> p("猪八戒", 100);person_show(p);return 0;
}

总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别 

类模板案例

案例描述: 实现一个通用的数组类,要求如下
。可以对内置数据类型以及自定义数据类型的数据进行存储
。将数组中的数据存储到堆区
。构造函数中可以传入数组的容量
。提供对应的拷贝构造函数以及operator=防止浅拷贝问题。

。提供尾插法和尾删法对数组中的数据进行增加和删除。

。可以通过下标的方式访问数组中的元素。

。可以获取数组中当前元素个数和数组的容量

代码 

myArray.hpp

#pragma
#include <iostream>
using namespace std;template<class T>
class myArray {
public:myArray(int capacity);myArray(myArray& p);~myArray();//重载=,防止浅拷贝myArray& operator=(const myArray& p) {if (this->Array != NULL) {delete[] this->Array;this->Array = NULL;}this->capacity = p.capacity;this->size = p.size;this->Array = p.Array;for (int i = 0; i < this->size; ++i) {this->Array[i] = p.Array[i];}}T& operator[](int index) {return this->Array[index];}void push_back(const T& val);void pop_back();int getsize() {return this->size;}int getcapacity() {return this->capacity;}
private:T* Array;int size;int capacity;
};//拷贝构造函数,注意要用深拷贝
template<class T>
myArray<T>::myArray(myArray& p) {this->size = p.size;this->capacity = p.capacity;this->Array = new T[this->capacity];for (int i = 0; i < this->size; ++i) {this->Array[i] = p.Array[i];}
}//有参构造函数
template<class T>
myArray<T>::myArray(int capacity) {this->capacity = capacity;this->Array = new T[this->capacity];this->size = 0;
}//析构函数
template<class T>
myArray<T>::~myArray() {if (this->Array != NULL) {delete[] this->Array;this->Array = NULL;}	
}template<class T>
void myArray<T> :: push_back(const T& val) {if (this->size == this->capacity) {return;}this->Array[this->size++] = val;
}template<class T>
void myArray<T> ::pop_back() {if (this->size == 0) {return;}--this->size;
}

main.cpp

#include "myArray.hpp"
class person {
public:string name;int age;person() {}person(string name, int age) {this->name = name;this->age = age;}
};void myprintf(myArray<person>& p) {for (int i = 0; i < p.getsize(); ++i) {cout << p[i].name << "  " << p[i].age << endl;}
}void fun() {person p1("小明", 100);person p2("小壮", 130);person p3("小聪", 103);person p4("小红", 1560);person p5("小懒", 190);//调用默认构造函数myArray<person> p(10);p.push_back(p1);p.push_back(p2);p.push_back(p3);p.push_back(p4);p.push_back(p5);cout << p.getsize() << endl;cout << p.getcapacity() << endl;myprintf(p);p.pop_back();cout << "弹出最后一个" << endl;myprintf(p);cout << "判断拷贝构造函数" << endl;myArray<person> p6(p);myprintf(p6);cout << "判断operator=" << endl;myArray<person> p7(0);p7 = p;myprintf(p7);
}
int main() {fun();return 0;
}

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

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

相关文章

宋浩概率论笔记(八)假设检验

宋浩系列全系列的最后一更&#xff01; 本章考察频率很低&#xff0c;核心在于记忆检验不同参数时用到的分布~

【项目实战】【已开源】USB2.0 HUB 集线器的制作教程(详细步骤以及电路图解释)

写在前面 本文是一篇关于 USB2.0 HUB 集线器的制作教程&#xff0c;包括详细的步骤以及电路图解释。 本文记录了笔者制作 USB2.0 HUB 集线器的心路历程&#xff0c;希望对你有帮助。 本文以笔记形式呈现&#xff0c;通过搜集互联网多方资料写成&#xff0c;非盈利性质&#xf…

许战海战略文库|品类缩量时代:制造型企业如何跨品类打造份额产品?

所有商业战略的本质是围绕着竞争优势与竞争效率展开的。早期&#xff0c;所有品牌立足于从局部竞争优势出发。因此,品牌创建初期大多立足于单个品类。后期增长受限,就要跨品类持续扩大竞争优势&#xff0c;将局部竞争优势转化为长期竞争优势&#xff0c;如果固化不前很难获得增…

腾讯mini项目-【指标监控服务重构】2023-07-19

今日已办 OpenTelemetry Logs 通过日志记录 API 支持日志收集 集成现有的日志记录库和日志收集工具 Overview 日志记录 API - Logging API&#xff0c;允许您检测应用程序并生成结构化日志旨在与其他 telemerty data&#xff08;例如metric和trace&#xff09;配合使用&am…

Java代码审计16之fastjson反序列化漏洞(1)

文章目录 1、简介fastjson2、fastjson的使用2.1、将类序列化为字符串2.2、将字符串还原为对象2.3、小结以上2.4、稍微扩展思路 3、fastjson漏洞利⽤原理与dnslog4、JdbcRowSetImpl利用链4.1、JdbcRowSetImpl的基本知识4.2、利用代码复现4.3、生成poc4.4、模拟真实场景4.5、利用…

MongoDB差异数据对比的快速指南

MongoDB是一种非关系型数据库&#xff0c;它以灵活的 JSON-like 文档的形式存储数据&#xff0c;这种特性使其在处理大量数据和实现快速开发时更具有优势。而由于其灵活的数据模型和强大的性能&#xff0c;MongoDB 被广泛应用在各种业务场景中。随着业务的发展和数据的增长&…

Excel VLOOKUP 初学者教程:通过示例学习

目录 前言 一、VLOOKUP的用法 二、应用VLOOKUP的步骤 三、VLOOKUP用于近似匹配 四、在同一个表里放置不同的VLOOKUP函数 结论 前言 Vlookup&#xff08;V 代表“垂直”&#xff09;是 excel 中的内置函数&#xff0c;允许在 excel 的不同列之间建立关系。 换句话说&#x…

iPhone苹果15手机怎么看是国行还是美版或港版的苹果iPhone15手机?

iPhone苹果手机15机型区域版本识别代码 CH代码为国行 LL代码为美版 ZP代码为港版 iPhone苹果15手机怎么看是国行还是美版或港版的苹果iPhone15手机&#xff1f; 1、打开苹果iPhone15手机桌面上的「设置」&#xff1b; 2、在iPhone苹果15手机设置内找到「通用」并点击打开&…

【OJ比赛日历】快周末了,不来一场比赛吗? #09.16-09.22 #12场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2023-09-16&#xff08;周六&#xff09; #3场比赛2023-09-17…

【FPGA项目】进阶版沙盘演练——报文收发(报文处理、CDC、CRC)

前言 书接上文【FPGA项目】沙盘演练——基础版报文收发_子墨祭的博客-CSDN博客&#xff0c;前面我们做了基础版的报文收发&#xff0c;相信对逻辑设计有了一定的认知&#xff0c;在此基础上&#xff0c;继续完善一个实际报文收发可能会遇到的一些处理&#xff1a; 报文处理握手…

公交查询系统

目录 需求分析 1 概述 2 课题分析 3 实现功能步骤 4 项目背景 概要设计 1 系统流程图. 2 功能模块. 3 各功能模块 4 数据存储 5 类设计 三、详细设计 1公交线路查询系统用户界面 2公交信息存储模快 3公交信息查询模块 4用户信息输入和输出模块 四、调试分析 五、使用说明 六、…

STM32外部复位IC与看门狗冲突,无法复位问题解决方案

使用STM32H743制作了一款飞控&#xff0c;外部复位IC采用MAX809STR,打板完后&#xff0c;烧录飞控固件后大量板子无法正常启动&#xff0c;怀疑是晶振没有起振或MCU未焊接好&#xff0c;检查后均焊接正常&#xff0c;编写裸机LED定时闪烁验证程序可正常运行。经网上查询资料锁定…

Python 环境搭建,集成开发环境IDE: PyCharm

Python 环境搭建,集成开发环境IDE: PyCharm 一、Python 环境搭建二、Python下载三、Python安装四、环境变量配置五、Python 环境变量六、运行Python1、交互式解释器&#xff1a;2、命令行脚本3、集成开发环境&#xff08;IDE&#xff1a;Integrated Development Environment&am…

Hadoop-Hive

1. hive安装部署 2. hive基础 3. hive高级查询 4. Hive函数及性能优化 1.hive安装部署 解压tar -xvf ./apache-hive-3.1.2-bin.tar.gz -C /opt/soft/ 改名mv apache-hive-3.1.2-bin/ hive312 配置环境变量&#xff1a;vim /etc/profile #hive export HIVE_HOME/opt/soft/hive…

软件测试的基本流程是什么?软件测试流程详细介绍

软件测试和软件开发一样&#xff0c;是一个比较复杂的工作过程&#xff0c;如果无章法可循&#xff0c;随意进行测试势必会造成测试工作的混乱。为了使测试工作标准化、规范化&#xff0c;并且快速、高效、高质量地完成测试工作&#xff0c;需要制订完整且具体的测试流程。 01…

JavaScript的DOM操作(二)

一、元素的特性attribute 1.元素的属性和特性 前面我们已经学习了如何获取节点&#xff0c;以及节点通常所包含的属性&#xff0c;接下来我们来仔细研究元素Element。 我们知道&#xff0c;一个元素除了有开始标签、结束标签、内容之外&#xff0c;还有很多的属性&#xff0…

【算法与数据结构】450、LeetCode删除二叉搜索树中的节点

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题首先要分析删除节点的五种情况&#xff1a; 1、没有找到节点2、找到节点 左右子树为空左子树为空…

微信小程序项目开发Day1

没接触过&#xff0c;直接看视频学习&#xff1a; 千锋教育微信小程序开发制作前端教程&#xff0c;零基础轻松入门玩转微信小程序_哔哩哔哩_bilibili千锋教育微信小程序开发制作前端教程&#xff0c;零基础轻松入门玩转微信小程序共计56条视频&#xff0c;包括&#xff1a;学…

软件工程课件

软件工程 考点概述软件工程概述能力成度模型能力成熟度模型集成软件过程模型逆向工程软件需求需求获取数据流图 需求定义 考点概述 重点章节 软件工程概述 之前老版教程的&#xff0c;之前考过 能力成度模型 记忆 能力等级 和 特点 能力成熟度模型集成 相比于CMM&#xff0c;第…

结合el-input、el-select实现纯前端过滤树形el-table数据

样式图示 1.搜索实现方法 const searchBtn async () > {// 获取table列表数据接口const res await Api.menuList({paging: false})if (res.code 200) {// 把树形结构转成扁平结构let result treeToArray(res.data)// 处理搜索框中数据进行table显示项过滤if(commonData…