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

C++提高--模板(类模板/函数模板)

模板的概念

函数模板(将类型参数化)

函数模板语法

两个函数逻辑非常相似

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;
// 模板// 交换两个数
void swapInt(int& a, int& b)
{int temp = a;a = b;b = temp;
}
void swapDouble(double& a, double& b)
{double temp = a;a = b;b = temp;
}// 可以发现,上面两个函数都是同样的逻辑,所以就要用到模板的概念
template<typename T>//声明一个模板,T是一个类型
void swapT(T& a, T& b)
{T temp = a;a = b;b = temp;
}
void test01()
{int a = 10;int b = 20;cout << "a = " << a << endl<< "b = " << b << endl;// 自动类型推到swapT(a, b);cout << "a = " << a << endl<< "b = " << b << endl;double c = 1.1;double d = 2.2;cout << "c = " << c << endl<< "d = " << d << endl;// 显示指定类型swapT<double>(c, d);cout << "c = " << c << endl<< "d = " << d << endl;
}
int main()
{test01();system("pause");return 0;
}

函数模板注意事项

总结: 使用时要先确定T,且确定的T是唯一的

函数模板案例

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;
template<typename T>
void sort(T* arr, int n)
{int i = 0;int j = 0;for (i = 0; i < n; i++){int max = i;for (j = i + 1; j < n; j++){if (arr[j] > arr[max]){max = j;}}if (max != i){swap(arr[max], arr[i]);}}
}template<typename T>
void swap(T& a, T& b)
{T temp = a;a = b;b = temp;
}template<typename T>
void myPrint(T* arr, int n)
{for (int i = 0; i < n; i++){cout << arr[i] << "  ";}cout << endl;
}void test01()
{int arr[] = { 9,8,7,2,5,10 };sort(arr, sizeof(arr) / sizeof(arr[0]));myPrint(arr, sizeof(arr) / sizeof(arr[0]));
}void test02()
{char arr[] = "snhfkbzy";sort(arr, sizeof(arr) / sizeof(arr[0]));myPrint(arr, sizeof(arr) / sizeof(arr[0]));
}int main()
{test01();test02();system("pause");return 0;
}

普通函数和模板的区别

智能但不够只能,只能想到一层

普通函数与函数模板的调用规则

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;void myPrint(int a,int b)
{cout << "普通函数的调用" << endl;
}template<typename T>
void myPrint(T a, T b)
{cout << "函数模板的调用" << endl;
}template<typename T>
void myPrint(T a, T b, T c)
{cout << "函数模板重载的调用" << endl;
}void test01()
{// 如果函数模板和普通函数都可以实现,优先调用普通函数myPrint(10, 20);// 可以通过空模板参数列表强制调用函数模板myPrint<>(10, 20);// 函数模板可以发生重载myPrint(10, 20, 30);// 如果函数模板可以产生更好的匹配性,优先调用函数模板char c = 'a';char d = 'b';// 普通函数可以调用,因为可以类型转换// 但模板具有更好的匹配性myPrint(c, d);
}
int main()
{test01();system("pause");return 0;
}

模板的局限性

也就是公用的模板个别特殊处理

STL--标准模板库

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<string>
using namespace std;class Person
{
public:Person(string name, int age){this->name = name;this->age = age;}string name;int age;
};template<typename T>
bool myCompare(T a, T b)
{if (a == b){return true;}else{return false;}
}template<>bool myCompare(Person p1, Person p2)
{if (p1.name == p2.name && p1.age == p2.age){return true;}else{return false;}
}void test01()
{int a = 10;int b = 10;bool ret = myCompare(a, b);if (ret){cout << "a == b" << endl;}else{cout << "a != b" << endl;}
}void test02()
{Person p1("tom", 18);Person p2("tom", 18);// 自定义数据类型,系统无法比较,需要将模板特例化bool ret = myCompare(p1, p2);if (ret){cout << "p1 == p2" << endl;}else{cout << "p1 != p2" << endl;}
}
int main()
{test01();test02();system("pause");return 0;
}

类模板

类模板的基本语法

// 类模板必须标出类型,系统不自动匹配

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<string>
using namespace std;template<class nameType,class ageType>
class Person
{
public:Person(nameType name, ageType age){this->name = name;this->age = age;}nameType name;ageType age;void showMessage(){cout << "姓名:" << this->name << endl<< "年龄:" << this->age << endl;}
};void test01()
{// 类模板必须标出类型,系统不自动匹配Person<string, int> p1("孙悟空", 999);p1.showMessage();
}int main()
{test01();system("pause");return 0;
}

类模板与函数模板的区别

默认参数,有个默认参数定义时可以不写

// 默认参数必须定义最后几个位置,且是连续的,且必须包含最后一个位置
// 否则语法是通过的,但是在调用的时候默认的位置还是不能省略
template<class nameType = string,class ageType = int,class idType>

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<string>
using namespace std;// 默认参数必须定义最后几个位置,且是连续的,且必须包含最后一个位置
// 否则语法是通过的,但是在调用的时候默认的位置还是不能省略
template<class nameType = string,class ageType = int,class idType>
class Person
{
public:Person(nameType name, ageType age, idType id){this->name = name;this->age = age;this->id = id;}nameType name;ageType age;idType id;void showMessage(){cout << "姓名:" << this->name << endl<< "年龄:" << this->age << endl<< "ID:" << this->id << endl;}
};void test01()
{// 类模板必须标出类型,系统不自动匹配Person<string, int, int>p1("孙悟空", 999,123);p1.showMessage();
}int main()
{test01();system("pause");return 0;
}

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

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;class Person1
{
public:void show1(){cout << "Person1函数调用" << endl;}
};class Person2
{
public:void show2(){cout << "Person2函数调用" << endl;}
};template<class T>
class Person
{
public:T obj;void func1(){obj.show1();}void func2(){obj.show2();}
};void test01()
{Person<Person1> p;p.func1();// 编译时没出错是因为编译器也不知道T是什么类型// 只有调用的时候该函数才生成// 而T具有唯一性,故代码运行是出错//p.func2();
}int main()
{test01();system("pause");return 0;
}

类模板对象做函数参数

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<string>
using namespace std;
//类模板对象做函数参数
template<class T1,class T2>
class Person
{
public:Person(T1 name, T2 age){this->name = name;this->age = age;}T1 name;T2 age;void show(){cout << "姓名:" << this->name << endl<< "年龄:" << this->age << endl;}
};// 1.指定传入类型 --直接显示对象的数据类型
// 这种方法在开发时使用最多
void myPrint1(Person<string, int> p)
{p.show();
}
void test01()
{Person<string, int> p1("孙悟空", 100);myPrint1(p1);
}// 2.参数模板化,将对象中的参数变为模板进行传递
template<class T1,class T2>
void myPrint2(Person<T1,T2> p)
{p.show();cout << "T1的类型为:" << typeid(T1).name() << endl<< "T2的类型为:" << typeid(T2).name() << endl;
}
void test02()
{Person<string, int> p2("猪八戒", 90);myPrint2(p2);
}// 3. 将整个类模板化
template<class T>
void myPrint3(T p)
{p.show();cout << "T的类型为:" << typeid(T).name() << endl;
}
void test03()
{Person<string, int> p3("唐僧", 30);myPrint3(p3);
}
int main()
{//test01();//test02();test03();system("pause");return 0;
}

类模板与继承

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;template<class T>
class Father
{
public:T name;
};
// 1.当子类继承父类模板的时候,子类在声明的时候,要指出父类中T的类型
// 否则他无法计算类的大小
//class Son :public Father// 2.可以单独指出父类的T
// 这样子类就不用模板化
class Son :public Father<string>
{};// 3.如果想灵活指定父类中的T类型,子类也需要模板化
template<class T1, class T2>
class Son2 :public Father<T2>
{
public:Son2(){cout << "T1的类型为:" << typeid(T1).name() << endl;cout << "T2的类型为:" << typeid(T2).name() << endl;}T1 obj;
};void test01()
{Son2<int, char> s2;
}
int main()
{test01();system("pause");return 0;
}

类模板成员函数类外实现

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;
template<class T1,class T2>
class Person
{
public:Person(T1 name, T2 age);T1 name;T2 age;void show();
};template<class T1, class T2>
// Person<T1,T2>表明他是类模板内的函数
Person<T1, T2>::Person(T1 name, T2 age)
{this->name = name;this->age = age;
}template<class T1, class T2>
void Person<T1, T2>::show()
{cout << "姓名:" << this->name << endl<< "年龄:" << this->age << endl;
}void test01()
{Person<string, int> p("张三",20);p.show();
}
int main()
{test01();system("pause");return 0;
}

类模板分文件编写

类模板分文件编写.cpp

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;// 出错的原因是类模板的成员函数只有在调用是才能生成
// 在编译的时候没生成这些成员函数,所以在链接的时候找不到这些成员函数
// 所以说在编译的时候编译器只走了Person.h这个文件
// 并没有看Person.cpp 中的函数
//#include"Person.h"// 1.第一种解决方法
// 因为Person.cpp中包含Person.h文件,所以编译器在编译时
// 不仅浏览了头文件,也浏览了成员函数的实现
//#include"Person.cpp"// 2.将.h文件和.cpp文件写在一起,将后缀名改为.hpp文件
// 约定俗成.hpp文件为类模板的头文件
#include"Person.hpp"
void test01()
{Person<string, int> p("张三", 20);p.show();
}
int main()
{test01();system("pause");return 0;
}

Person.h

//#pragma once
//#include<iostream>
//using namespace std;
//template<class T1, class T2>
//class Person
//{
//public:
//	Person(T1 name, T2 age);
//	T1 name;
//	T2 age;
//	void show();
//};

Person.cpp

//#include"Person.h"
//
//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>::show()
//{
//	cout << "姓名:" << this->name << endl
//		<< "年龄:" << this->age << endl;
//}

Person.hpp

#pragma once
#include<iostream>
using namespace std;template<class T1, class T2>
class Person
{
public:Person(T1 name, T2 age);T1 name;T2 age;void show();
};
#include"Person.h"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>::show()
{cout << "姓名:" << this->name << endl<< "年龄:" << this->age << endl;
}

类模板与友元

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;// 告诉编译器友这个类
template<class T1, class T2>
class Person;// 因为Person<T1, T2> p,所以前面要告诉编译器有这个类
template<class T1, class T2>
void show2(Person<T1, T2> p);// 因为friend void show2<>(Person<T1, T2> p);,所以上面要告诉编译器有这个函数
template<class T1,class T2>
class Person
{// 1.全局函数类内实现friend void show(Person<T1,T2> p){cout << "类内实现-->  姓名:" << p.name<< "年龄:" << p.age << endl;}// 2全局函数类外实现// 如果不加<>就表明是普通函数的声明,而不是模板函数// 这里不用写template<class T1,class T2>是因为和类共用了;friend void show2(Person<T1, T2> p);// 或者这样也行//template<class T1, class T2>//friend void show2(Person<T1, T2> p);
public:Person(T1 name, T2 age){this->name = name;this->age = age;}
private:T1 name;T2 age;
};template<class T1, class T2>
void show2(Person<T1, T2> p)
{cout << "类外实现-->  姓名:" << p.name<< "年龄:" << p.age << endl;
}// 1.全局函数友元类内实现
void test01()
{Person<string, int> p("张三", 20);show(p);
}// 2.全局函数友元类外实现
void test02()
{Person<string, int> p("张三", 20);show2(p);
}
int main()
{test01();test02();system("pause");return 0;
}

类模板案例

类模板案例-数组类封装.hpp

#pragma once
#include<iostream>
using namespace std;
// 可以对内置数据类型和自定义数据类型进行存储
template<class T>
class MyArry
{
public:// 构造函数可以传入数组的容量MyArry(int capacity){//cout << "参数构造函数的调用" << endl;this->capacity = capacity;this->size = 0;// 将数组中的数据存储到堆区this->arr = new T[this->capacity];}// 提供拷贝构造函数防止浅拷贝的问题// 浅拷贝的问题也就是堆区数据重复释放MyArry(const MyArry& arr){//cout << "拷贝函数的调用" << endl;this->capacity = arr.capacity;this->size = arr.size;this->arr = new T[this->capacity];int i = 0;for (i = 0; i < this->size; i++){this->arr[i] = arr.arr[i];}}// operator=防止浅拷贝的问题MyArry& operator=(MyArry& arr){//cout << "operator=的调用" << endl;if (this->arr != NULL){delete[] this->arr;this->capacity = 0;this->size = 0;//this->arr = NULL;}this->capacity = arr.capacity;this->size = arr.size;this->arr = new T[this->capacity];int i = 0;for (i = 0; i < this->size; i++){this->arr[i] = arr.arr[i];}return *this;}//可以通过下标的方式访问数组中的数据T& operator[](int i){return this->arr[i];}// 尾插法void tailInter(const T& val){if (this->capacity == this->size){cout << "内存满" << endl;return;}else{this->arr[this->size] = val;this->size++;}}// 尾删法void tailDelete(){if (this->size == 0){return;}this->size--;}// 获取当前数组容量int getCapacity(){return this->capacity;}// 获取当前元素个数int getSize(){return this->size;}// 析构函数对堆区内存释放~MyArry(){if (this->arr != NULL){//cout << "析构函数的调用" << endl;delete[] this->arr;this->arr = NULL;this->capacity = 0;this->size = 0;}}private:T* arr; // 在堆区创建数组int capacity;// 数组容量int size;// 数组实际内容个数
};

类模板案例-数组类封装.cpp

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<string>
using namespace std;
#include"类模板案例-数组类封装.hpp"void myPrint(MyArry<int>& p)
{for (int i = 0; i < p.getSize(); i++){cout << p[i] << endl;}
}
void test01()
{MyArry<int> p1(5);int i = 0;for (i = 0; i < 5; i++){p1.tailInter(i);}myPrint(p1);p1.tailDelete();cout << p1.getCapacity() << endl<< p1.getSize() << endl;
}
// 自定义数据类型
class Person
{
public:Person() {};Person(string name, int age){this->name = name;this->age = age;}string name;int age;
};void myPrint2(MyArry<Person>& p)
{for (int i = 0; i < p.getSize(); i++){cout << "姓名:" << p[i].name<< "年龄:" << p[i].age << endl;}
}
void test02()
{MyArry<Person> p(10);p.tailInter(Person("张三",13));p.tailInter(Person("lisi", 14));p.tailInter(Person("wangwu", 15));p.tailInter(Person("zhangliu", 16));myPrint2(p);
}
int main()
{//test01();test02();system("pause");return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 数据库第一章:库的操作
  • linux-IO-进程-线程(相关函数)
  • Apache POI用法
  • 云原生应用——软件的未来
  • JVM-内存区域
  • 打造古风炫酷个人网页:用HTML和CSS3传递笔墨韵味
  • 基于Ubuntu+PostgreSQL+Zip搭建SonarQube环境
  • linux的文本编辑器vim常用操作命令介绍
  • 西门子S7协议(PROFINET端口)转罗克韦尔AB的Ethernet/IP网络通讯
  • ubuntu16.04下qt5.7.1添加对openssl的支持
  • C# 手动写入日志,过大写入新文件
  • 走进低代码表单开发(三):高效业务功能构建
  • 构建高效入学审核系统:Spring Boot解决方案
  • 【经验技巧】瞬态信号仿真中的码型选择问题
  • Windows桌面整理软件哪个最好?值得一试的Top10桌面管理软件汇总(全新)
  • php的引用
  • [PHP内核探索]PHP中的哈希表
  • [Vue CLI 3] 配置解析之 css.extract
  • CODING 缺陷管理功能正式开始公测
  • CSS相对定位
  • CSS中外联样式表代表的含义
  • Intervention/image 图片处理扩展包的安装和使用
  • Java小白进阶笔记(3)-初级面向对象
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • LintCode 31. partitionArray 数组划分
  • Protobuf3语言指南
  • Redis 中的布隆过滤器
  • Theano - 导数
  • v-if和v-for连用出现的问题
  • web标准化(下)
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • 大整数乘法-表格法
  • 简单数学运算程序(不定期更新)
  • 开发基于以太坊智能合约的DApp
  • 两列自适应布局方案整理
  • 使用API自动生成工具优化前端工作流
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • #QT(串口助手-界面)
  • (20050108)又读《平凡的世界》
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (day18) leetcode 204.计数质数
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (翻译)Quartz官方教程——第一课:Quartz入门
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (四)Android布局类型(线性布局LinearLayout)
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • ***利用Ms05002溢出找“肉鸡
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .NET Core 中插件式开发实现
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET 药厂业务系统 CPU爆高分析
  • .NET 指南:抽象化实现的基类