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

news/2024/9/18 1:51:15 标签: 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;
}


http://www.niftyadmin.cn/n/5658332.html

相关文章

2-94 基于matlab的最佳维纳滤波器的盲解卷积算法

基于matlab的最佳维纳滤波器的盲解卷积算法。维纳滤波将地震子波转换为任意所需要的形态。维纳滤波不同于反滤波&#xff0c;它是在最小平方的意义上为最 佳。基于最佳纳滤波理论的滤波器算法是莱文逊(Wiener—Levinson)算法。程序提供了4种子波和4种期望输出&#xff1a;零延迟…

SOME/IP通信协议在汽车业务具体示例

标签&#xff1a;SOME/IP&#xff1b; SomeIP通信协议在汽车业务具体示例&#xff1b; SomeIP通信协议在汽车业务具体示例 SOME/IP&#xff08;Scalable service-Oriented MiddlewarE over IP&#xff09;协议被广泛应用于现代汽车的多个关键业务领域。SOME/IP协议特别适合需要…

电商下载图片工具亚马逊图片一键下载

优美图片提升商品吸引力&#xff0c;增强购买欲望。在电商平台&#xff0c;好图即好销量。高清、专业的照片显品质&#xff0c;赢信任&#xff0c;促成交。视觉营销关键&#xff0c;助力品牌塑造&#xff0c;提升竞争力。简而言之&#xff0c;美图是电商之窗&#xff0c;展示价…

【MySQL】查询表中重复数据、模糊查询列信息、快速copy表数据(1)

一、SQL查询重复的数据&#xff1a; 1、SQL格式&#xff1a; Select * From 数据表 Where 重复记录字段 in ( select 重复记录字段 From 数据表 Group By 重复记录字段 Having Count(重复记录字段)>1) 2、举例&#xff1a; 在这个patient_member_info表中&#xff0c;我们…

【卷起来】VUE3.0教程-07-异步请求处理(springboot后端)

&#x1f332; 服务端接口准备 pom文件&#xff0c;引入mybatis/mybatis-plus相关依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>&…

k8s的环境配置

一、前期系统环境准备 准备3台主机&#xff1a;硬盘50G cpu2个 内存2G 1、3台主机同时配置 1&#xff09;关闭防火墙与selinux、NetworkManager [rootk8s-master ~]# systemctl stop firewalld[rootk8s-master ~]# systemctl disable firewalldRemoved symlink /etc/systemd/…

vue3使用leaflet+trackplayer实现非地图动画轨迹(市场平面图动态轨迹)

vue3使用leaflettrackplayer实现非地图动画轨迹(市场平面图动态轨迹) 先下载 leaflet 和 leaflet-trackplayer两个主要库 leaflet官方文档 npm install leaflet npm install leaflet-trackplayer然后在页面中引用 html <template><button click"playMap&quo…

资深盘点:国内外知名的ERP管理系统厂商有哪些?

已经考虑引入ERP系统却担心面临以下问题&#xff1f;ERP系统流程僵化难以调整&#xff1f;流程与实际业务脱节&#xff1f;培训不到位、技术支持不及时导致难以实现全员使用、共创&#xff1f;市面上的ERP系统众多不知道如何选择&#xff1f; 今天就根据2024最新市场动态&…