C++模板(泛型)

1. 模板

1.1 知识点

模板:template

泛型编程:

是指数据的类型是广泛,任意的数据类型

模板:可以将一个函数或类描述成一个模板,例如:画画,给一个人物模型上色彩,根据用户上的色彩是什么人物显示什么样的色彩

模板分为 函数模板  和  类模板

1.1.1 注意

(1)template用于声明一个模板

(2)typename/class来声明模板形参,模板形参是指传递给该函数的参数类型(模板形参就是该函数的参数类型),指定该函数里用的模板形参的类型

(3)模板形参要与函数形参一一对应

1.2 函数模板

关键字:template、class/typename

格式:


	template   <模板形参1,模板形参2,........>函数返回值   函数名   ([参数1,参数2,.......])
	{

	}
	template   <class T1,class T2,........>函数返回值   函数名   ([参数1,参数2,.......])
	{
		
	}
	template   <typename T1,typename T2,........>函数返回值   函数名   ([参数1,参数2,.......])
	{

	}	

1.2.1 函数模板的调用

与C/C++普通函数调用一致:

隐式调用

格式:
		函数名(实参1,实参2);//隐式调用(模板形参)
例如:
		sum(10,20);

函数模板的显示调用(模板形参):

格式:
		函数名 <数据类型1,数据类型2,.......>(实参1,实参2,......);
例如:
		sum<int ,   char> (10,'a');

1.2.1.1 隐式调用

wide_practise.cpp

#include "iostream"
using namespace std;

//模板(泛型)

//声明函数模板写法1
template <class T1, class T2> void sum(T1 a,T2 b) {
	T1 m = a;
	T2 n = b;
	cout << "m= " << m << endl;
	cout << "n= " << n << endl;
}

声明函数模板写法2
//template <typename T1, typename T2> T1 sum(T1 a, T2 b) {
//	
//}
//
声明函数模板写法3
//template <typename T1, typename T2> T2 sum(T1 a, T2 b) {
//	
//}

int main()
{
	sum(10, 20);
	sum(10.2, 3.14);
	sum(10,"hello world");
	sum("hello","world");
	sum("你好",10);
	return 0;
}

1.2.1.2 显示调用

wide_practise.cpp

//模板(泛型)显示调用
//声明函数模板写法1
template <class T1, class T2> void sum(T1 a, T2 b) {
	T1 m = a;
	T2 n = b;
	cout << "m= " << m << "  n= " << n << endl;
}

int main()
{
	sum(10,20);//隐式调用
	sum<int, int>(50, 60);//显示调用
	sum<int, char>(40, 'a');
	return 0;
}

1.3.3 自定义类型调用

wide_practise.cpp
//模板(泛型)自定义类型
class People {
public:
	int num = 90;

};

//声明模板函数
template <class T1, class T2> void sum(T1 a, T2 b) {

	cout << "自定义类型的a= " << a.num << "  b= " << b << endl;
}

int main()
{
	People people;
	sum(people, 80);//隐式调用
	sum<People, int>(people, 55);//显示调用

	return 0;
}

1.4 练习

编写如下形式的模板: template <class Type>void sort(Type* A, int n, bool f)

对一个具有n个Type类型的一维数组进行排序,f伪1表示从小到大排序, f为0表示从大到小排序

要求使用快速排序

wide_test.cpp

#include "iostream"
#include <stdbool.h>
using namespace std;


//编写如下形式的模板: template <class Type>void sort(Type* A, int n, bool f)
//对一个具有n个Type类型的一维数组进行排序,f伪1表示从小到大排序, f为0表示从大到小排序
//快速排序

//使用快速排序
//将快速排序也声明为模板函数
template<class Type> void quick_sort(Type arr[],int left,int right) {

	//跳出递归的条件
	if (left>=right) {
		return;
	}

	//左右哨兵
	int l = left;
	int r = right;

	//定义基准数
	Type base = arr[left];

	while (l!=r) {
		
		//右哨兵先走,遇见比基准数小的就停下(即比基准数大就走动)
		while(l<r && arr[r]>=base) {
			r--;
		}
		//接着左哨兵走,遇见比基准数大的就停下(即比基准数小就走动)
		while (l<r && arr[l]<=base) {
			l++;
		}

		//当两个哨兵都停且没有相遇,就交换两个哨兵所指向的数据
		if (l<r) {
			Type tmp = arr[l];
			arr[l] = arr[r];
			arr[r] = tmp;
		}

	}

	//两个哨兵相遇时,使哨兵所指的数据与基准数交换
	//至此到这,交换后的基准数,左边都比自己小,右边都比自己大
	if (l==r) {
		Type tmp = arr[left];
		arr[left] = arr[r];
		arr[r] = tmp;
	}

	//进入递归
	quick_sort<Type>(arr,left,r-1);
	quick_sort<Type>(arr,r+1,right);
}

//声明模板
template<class Type> void sort(Type* arr, int n, bool f) {

	if (f) {//f为真正序排序

		//将快速排序也声明为模板函数
		quick_sort<Type>(arr,0,n-1);
	}
	else {//为假倒叙排序
		//我们现正排,然后反转数组,达到倒序排序的效果
		quick_sort<Type>(arr, 0, n - 1);

		int r = n;
		//数组反转
		for (int i = 0; i < n/2;i++) {
			Type tmp = arr[i];
			arr[i] = arr[r-1];
			arr[r-1] = tmp;
			r--;
		}
	}
	
}


int main()
{

	//传递整型
	int arr[] = { 8,4,1,2,7,13,2,4,8 }; 
	int size = sizeof(arr)/sizeof(arr[0]);
	sort<int>(arr, size, 0);
	//遍历
	for (int i = 0; i < size;i++) {
		cout << arr[i]<<" ";
	}

	cout << endl;

	//传递字符型
	char arr2[] = { 'g','c','z','f','c','a'};
	int size2 = sizeof(arr2) / sizeof(arr2[0]);
	sort<char>(arr2, size2, 1);
	//遍历
	for (int i = 0; i < size2; i++) {
		cout << arr2[i] << " ";
	}

	cout << endl;

	//传递浮点型
	double arr3[] = { 88.5,22.6,12.7,3.14,2.17,9.38 };
	int size3 = sizeof(arr3) / sizeof(arr3[0]);
	sort<double>(arr3, size3, 0);
	//遍历
	for (int i = 0; i < size3; i++) {
		cout << arr3[i] << " ";
	}

	return 0;
}

2. 类模板

2.1 知识点

关键字:template、class/typename

格式:

	template  <模板形参1 , 模板形参2,非类型形参,......> class  类名
	{
		
	};
	template  <class T1 , class T2,int  sum,......> class  类名
	{
		
	};	
	template  <typename T1 , typename T2,int  sum,......> class  类名
	{
		
	};

2.1.1 注:

(1)class T1/T2等是模板形参,int  sum:非类型形参

非类型:是指实例化类的时候,不能传递类型,只能传递实参,非类型形参是整型int\short\long等等不能字符串或浮点型

2)<>里的class用于声明模板形参 ,  类名前的class用于声明一个类

2.2 举例

lei_template.cpp

#include "iostream"
using namespace std;

//声明模板类
//sum 这里的sum是非类型实参,传递时不能传递类型,要传递具体的值
template<class T1, class T2,int sum> class Animal {
public:
	Animal(T1 a,T2 b) {
		this->a = a;
		this->b = b;

	}
	void diplay() {
		cout << "a= " << a << " b= " << b << endl;
	}

	T1 a;
	T2 b;
	int arr[sum];//我们利用非类型实参,给这个数组属性定义大小
};

int main()
{
	//实例化类模板
	Animal<int, char,30> animal(10, 'v');
	animal.diplay();

	return 0;
}

2.3 练习

2.3.1 例1

我在一个类中定义了一个结构体,我在类中写了一个成员函数,返回值是结构体类型,我在主函数中使用此成员函数,该怎么接收返回值呢

test.cpp
#include <iostream>
using namespace std;

class MyClass {
public:
    // 定义一个结构体类型
    struct MyStruct {
        int data;
    };

    // 返回结构体类型的成员函数
    MyStruct getStruct() {
        MyStruct result;//定义结构类型
        result.data = 42;//给结构体属性赋值
        return result;//返回结构体类型
    }
};

int main() {
    // 实例化类对象
    MyClass myObject;

    // 调用成员函数并接收返回值
    // MyClass::MyStruct 访问嵌套的结构体类型
    //  MyClass::MyStruct 这种语法对于访问嵌套类型、静态成员或命名空间中的元素是很常见的。
    MyClass::MyStruct receivedStruct = myObject.getStruct();

    // 输出结构体成员的值
    cout << "Data in MyStruct: " << receivedStruct.data << endl;

    return 0;
}

2.2.2 例2

我的写法

定义一个堆栈类模板,实现不同数据类型数据的入栈和出栈操作,堆栈的大小用非类型参数指定。

lei_template_test.cpp

#include "iostream"
#include<stdbool.h>
using namespace std;

typedef bool status;
#define MAXSIZE 10
//定义一个堆栈类模板,实现不同数据类型数据的入栈和出栈操作,堆栈的大小用非类型参数指定。

//定义模板类
template<class T> class Stack_ {
public:

	struct Stack {
		T stack[MAXSIZE];
		int top;
	};

	//创建顺序栈
	struct Stack* Stack_Create() {
		struct Stack* p = (struct Stack*)malloc(sizeof(struct Stack));
		if (p == NULL) {
			return NULL;
		}

		//赋初值
		memset(p->stack, 0, sizeof(p->stack));
		p->top = -1;
		return p;

	}

	//入栈
	status Stack_Push(struct Stack* p, T data) {
		//判断栈是否满了
		if (p->top == MAXSIZE - 1) {
			printf("栈已满,入栈失败\n");
			return false;
		}

		p->top += 1;
		p->stack[p->top] = data;
		return true;
	}

	//出栈
	status Stack_Pop(struct Stack* p, T* data) {
		//判断栈是否为空
		if (p->top == -1) {
			printf("栈为空栈,出栈失败!\n");
			return false;
		}

		*data = p->stack[p->top];
		p->stack[p->top] = 0;//清空
		p->top--;
		return true;
	}

	//遍历栈
	status Stack_Travel(struct Stack* p) {
		//判断栈是否为空
		int top_1 = p->top;

		if (top_1 == -1) {
			printf("栈为空栈,遍历失败\n");
			return false;
		}
		while (top_1 >= 0) {
			cout << p->stack[top_1] << " ";
			top_1--;
		}
		return true;

	}

	// 释放堆栈
	void Stack_Destroy(struct Stack* p) {
		delete p;  // 使用 delete 进行动态内存释放
	}


};


int main()
{
	//栈中存入int类型
	//实例化类模板
	Stack_<int> stack_;
	Stack_<int>::Stack* p = stack_.Stack_Create();//Stack_<int>::Stack是在 C++ 中使用模板类 Stack 时访问内部嵌套结构 StackStruct 的方法。
	//入栈
	stack_.Stack_Push(p, 10);
	stack_.Stack_Push(p, 20);
	stack_.Stack_Push(p, 30);
	stack_.Stack_Push(p, 40);
	stack_.Stack_Push(p, 50);
	//遍历
	stack_.Stack_Travel(p);
	cout << endl;
	//出栈
	int data;
	stack_.Stack_Pop(p, &data);
	cout << "取出栈顶元素为:" << data << endl;
	//遍历
	stack_.Stack_Travel(p);
	//释放堆栈
	stack_.Stack_Destroy(p);


	cout << endl;
	cout << endl;


	//栈中存入char类型
	//实例化类模板
	Stack_<char> stack_2;
	Stack_<char>::Stack* p2 = stack_2.Stack_Create();//Stack_<int>::Stack是在 C++ 中使用模板类 Stack 时访问内部嵌套结构 StackStruct 的方法。
	//入栈
	stack_2.Stack_Push(p2, 'a');
	stack_2.Stack_Push(p2, 'b');
	stack_2.Stack_Push(p2, 'c');
	stack_2.Stack_Push(p2, 'd');
	stack_2.Stack_Push(p2, 'e');
	//遍历
	stack_2.Stack_Travel(p2);
	cout << endl;
	//出栈
	char data2;
	stack_2.Stack_Pop(p2, &data2);
	cout << "取出栈顶元素为:" << data2 << endl;
	//遍历
	stack_2.Stack_Travel(p2);
	//释放堆栈
	stack_2.Stack_Destroy(p2);

	cout << endl;
	cout << endl;

	//栈中存入double类型
	//实例化类模板
	Stack_<double> stack_3;
	Stack_<double>::Stack* p3 = stack_3.Stack_Create();//Stack_<int>::Stack是在 C++ 中使用模板类 Stack 时访问内部嵌套结构 StackStruct 的方法。
	//入栈
	stack_3.Stack_Push(p3, 3.14);
	stack_3.Stack_Push(p3, 8.25);
	stack_3.Stack_Push(p3, 1.22);
	stack_3.Stack_Push(p3, 7.45);
	stack_3.Stack_Push(p3, 6.489523);
	//遍历
	stack_3.Stack_Travel(p3);
	cout << endl;
	//出栈
	double data3;
	stack_3.Stack_Pop(p3, &data3);
	cout << "取出栈顶元素为:" << data3 << endl;
	//遍历
	stack_3.Stack_Travel(p3);
	//释放堆栈
	stack_3.Stack_Destroy(p3);

	return 0;
}

另一种写法

lei_template_test2.cpp

#include "iostream"
using namespace std;

//定义一个堆栈类模板,实现不同数据类型数据的入栈和出栈操作,堆栈的大小用非类型参数指定。
//
template<class T, int MAXSIZE>class Stack
{
public:
	Stack()
	{
		stack = new T[MAXSIZE]{ 0 };//申请空间
		this->top = -1;
	}
	//判断栈是否为空
	bool isEmpty()
	{
		if (this->top == -1)
		{
			return true;
		}
		return false;
	}
	//判断栈是否为满
	bool isFull()
	{
		if (this->top == MAXSIZE - 1)
		{
			cout << "栈已满!" << endl;
			return true;
		}
		return false;
	}
	//入栈
	bool Push(T data)
	{
		if (this->isFull())
		{
			return false;
		}
		this->top += 1;
		this->stack[this->top] = data;
		return true;
	}
	//出栈
	bool Pop(T& data)
	{
		if (this->isEmpty())
		{
			return false;
		}
		data = this->stack[this->top];
		this->stack[this->top] = 0;
		this->top--;
		return true;
	}
	//返回栈顶元素
	bool get_Top_Data(T& data)
	{
		if (this->isEmpty())
		{
			return false;
		}
		data = this->stack[this->top];
	}
	//打印栈中的数据
	void display()
	{
		for (int i = 0; i < MAXSIZE; i++)
		{
			cout <<this->stack[i] <<" ";
		}
	}
	~Stack()
	{
		delete[]stack;//释放资源
	}
private:
	int top;//指向栈顶指针
	T* stack;
};
int main()
{
	//实例化一个类 int类型的栈
	Stack<int, 10>stack;

	//看看栈是否已满
	bool flag = stack.isFull();
	if (flag) {

	}
	else {//不满添加数据
		stack.Push(10);
		stack.Push(20);
		stack.Push(30);

		stack.display();

	}
	
	//看看栈是否为空
	flag = stack.isEmpty();
	if (flag)
	{
		cout << "栈为空!" << endl;
	}
	else
	{
		cout << endl;
		int data;
		stack.Pop(data);
		cout << "取出栈顶元素为:" << data << endl;
		stack.display();

	}

	cout << endl;
	//实例化一个类,char类型的栈
	Stack<char, 10>stack2;
	//看看栈是否已满
	bool flag2 = stack2.isFull();
	if (flag2) {

	}
	else {//不满添加数据
		stack2.Push('a');
		stack2.Push('b');
		stack2.Push('c');

		stack2.display();

	}

	//看看栈是否为空
	flag2 = stack2.isEmpty();
	if (flag2)
	{
		cout << "栈为空!" << endl;
	}
	else
	{
		cout << endl;
		char data2;
		stack2.Pop(data2);
		cout << "取出栈顶元素为:" << data2 << endl;
		stack2.display();

	}

	return 0;
}

2.2.3 使用通用模板类写一个冒泡排序

bubble_sort.cpp
#include "iostream"
using namespace std;


//使用通用模板类写一个冒泡排序
class People {
public:
	People(char c) {
		str = c;
	}

	// 重载 > 运算符
	bool operator>(const People& other) const {
		return this->str > other.str;
	}

	char str;
};

template<class T> class b_sort {
public:
	void bubble_sort(T arr[], int sz) {
		for (int i = 0; i < sz - 1; i++) {
			for (int j = 0; j < sz - i - 1; j++) {

				if (arr[j] > arr[j + 1]) {
					T tmp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = tmp;
				}
			}
		}
	}

};


int main()
{
	//实例化一个对象
	//排序整型
	b_sort<int> sort;
	int arr[] = { 8,3,1,5,63,2,7,9};
	int sz = sizeof(arr) / sizeof(arr[0]);
	sort.bubble_sort(arr,sz);
	for (int i = 0; i < sz;i++) {
		cout << arr[i] << " ";
	}

	cout << endl;
	cout << endl;


	//实例化一个对象
	//排序字符型
	b_sort<char> sort2;
	char arr2[] = {'f','r','w','h','b','a'};
	int sz2 = sizeof(arr2) / sizeof(arr2[0]);
	sort2.bubble_sort(arr2, sz2);
	for (int i = 0; i < sz2; i++) {
		cout << arr2[i] << " ";
	}

	cout << endl;
	cout << endl;

	//实例化一个对象
	//排序自定义类型
	b_sort<People> sort3;
	People people1('l');
	People people2('z');
	People people3('l');
	People people4('i');
	People arr3[] = {people1,people2,people3,people4 };
	int sz3 = sizeof(arr3) / sizeof(arr3[0]);
	sort3.bubble_sort(arr3, sz3);
	for (int i = 0; i < sz3; i++) {
		cout << arr3[i].str << " ";
	}

	return 0;
}

  • 38
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值