C++基础08-函数模板template

01 函数模板的基本用法

模板概论:
C++提供了函数模板,所谓函数模板,实际上就是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就成为函数模板。
凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。

1. 自动类型推导,必须有参数类型才可以推导

2. 显示指定类型

3.模板必须要指定出T才可以使用

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//交换两个int类型的数据
void mySwapInt(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

//交换两个double类型的数据
void mySwapDouble(double& a, double& b)
{
	double tmp = a;
	a = b;
	b = tmp;
}

//类型,逻辑非常相似
//类型参数化 泛型编程——模板技术
template<class T>//告诉编译器 下面如果出现了T不要报错,T是一个通用的类型
void mySwap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

template<typename T>//等价于     template<class T>
void mySwap2()
{

}

void test01()
{
	int a = 10;
	int b = 20;
	//mySwapInt(a, b);
	//1. 自动类型推导,必须有参数类型才可以推导
	mySwap(a, b);

	//2. 显示指定类型
	mySwap<int>(a, b);

	//3.模板必须要指定出T才可以使用
	mySwap2<double>();

	cout << "a = " << a << "   b = " << b << endl;

	double c = 3.14;
	double d = 1.5;
	//mySwapDouble(c, d);
	mySwap(c, d);
	cout << "c = " << c << "   d = " << d << endl;
}

int main(void)
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

02 课堂练习-利用模板实现char和int的数组排序

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//对char和int类型数组进行排序,排序规则 从大到小 利用选择排序
template<class T>
void mySwap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

template<class T>
void mySort(T arr[], int len)
{
	for (int i = 0; i < len; ++i)
	{
		int max = i;
		for (int j = i + 1; j < len; ++j)
		{
			if (arr[max] < arr[j])
			{
				//交换下标
				max = j;
			}
			if (max != i)
			{
				mySwap(arr[max], arr[i]);
			}
		}
	}
}

template<class T>
void myPrintArray(T arr[], int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

void test01()
{
	int intArr[] = { 5,96,8,9,7,5,1,2,3,5654,4154,125315,2246 };
	int len1 = sizeof(intArr) / sizeof(intArr[0]);
	mySort<int>(intArr, len1);
	myPrintArray<int>(intArr, len1);

	char charArray[] = "helloworld";
	int len2 = sizeof(charArray) / sizeof(char);
	mySort(charArray, len2);
	myPrintArray(charArray, len2);
}

int main(void)
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

03 函数模板与普通模板的区别以及调用规则

1. 普通函数与函数模板的区别

类型推导不出来,函数模板不可以进行隐式类型转换
通函数可以进行隐式类型转换

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//1. 普通函数与函数模板的区别
template<class T>
T myPlus1(T a, T b)
{
	return a + b;
}

int myPlus2(int a, int b)
{
	return a + b;
}

void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';//'a' = 97

	//myPlus1(a, c);//类型推导不出来,函数模板不可以进行隐式类型转换
	cout << myPlus2(a, c) << endl;//普通函数可以进行隐式类型转换
}

在这里插入图片描述

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

  1. 如果出现重载,优先使用普通函数调用(如果没有实现,出现错误)
  2. 如果想强制调用模板,那么可以使用空参数列表
  3. 函数模板可以发生重载
  4. 如果函数模板可以产生更好的匹配,那么优先调用函数模板
template<class T>
void myPrint(T a, T b)
{
	cout << "模板函数调用的myPrint" << endl;
}

void myPrint(int a, int b)
{
	cout << "普通函数调用的myPrint" << endl;
}

template<class T>
void myPrint(T a, T b, T c)
{
	cout << "模板函数调用的myPrint(a,b,c)" << endl;
}

void test02()
{
	int a = 10;
	int b = 20;

	//1. 如果出现重载,优先使用普通函数调用(如果没有实现,出现错误)
	myPrint(a, b);

	//2. 如果想强制调用模板,那么可以使用空参数列表
	myPrint<>(a, b);

	//3. 函数模板可以发生重载
	int c = 30;
	myPrint(a, b, c);

	//4. 如果函数模板可以产生更好的匹配,那么优先调用函数模板
	char d = 'd';
	char e = 'e';
	myPrint(d, e);
}

04 模板机制

template<class T>
void myPrint(T a, T b, T c)
{
	cout << "模板函数调用的myPrint(a,b,c)" << endl;
}

//通过模板生成的函数叫模板函数
void myPrint(int a, int b, int c)
{

}

模板的机制:

模板并不是万能的,不能通过所有的数据类型
模板不能直接调用,生成后的模板函数才可以调用
二次编译,第一次对模板进行编译,第二次对替换T类型后的代码进行二次编译

05 模板的局限性以及解决

对象的比较出现问题。
通过具体化自定义数据类型,解决上述问题:

template<>返回值 函数名(具体类型参数)

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Age = age;
		this->m_Name = name;
	}

	string m_Name;
	int m_Age;
};

template<class T>
bool myCompare(T& a, T& b)
{
	if (a == b)
	{
		return true;
	}
	return false;
}

//通过具体化自定义数据类型,解决上述问题
//如果具体化能够优先匹配,那么就选择具体化
//语法:template<>返回值 函数名(具体类型参数)
template<>bool myCompare(Person& a, Person& b)
{
	if (a.m_Age == b.m_Age)
	{
		return true;
	}
	return false;
}

void test01()
{
	int a = 10;
	int b = 20;

	int ret1 = myCompare(a, b);
	cout << "ret1 = " << ret1 << endl;

	Person p1 = { "Tom",10 };
	Person p2 = { "Jerry",20 };

	int ret2 = myCompare(p1, p2);
	cout << "ret2 = " << ret2 << endl;
}

int main(void)
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

06 类模板的基本使用

写法:template<class T1,class T2···>
与函数模板的区别,可以有默认类型参数
函数模板可以进行自动类型推导,而类模板不可以

在这里插入代码片
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//类模板
template<class NameType, class AgeType=int>//与函数模板的区别,可以有默认类型参数
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson()
	{
		cout << this->m_Name << "的年龄是" << this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;

};

void test01()
{
	//自动类型推导,类模板,不支持
	//Person p("孙悟空", 100);

	//显示指定类型
	Person<string, int>p("孙悟空", 500);
	p.showPerson();
}

int main(void)
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

07 成员函数创建时机

成员函数 一开始不会创建出来,而是在运行的时候被创建出来

class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1的调用" << endl;
	}
};

class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2的调用" << endl;
	}
};


template<class T>
class myClass
{
public:
	T obj;
	void func1()
	{
		obj.showPerson1();
	}
	void func2()
	{
		obj.showPerson2();
	}
};

void test02()
{
	myClass<Person1>m;
	m.func1();
	//运行时才会报错
	m.func2();//成员函数 一开始不会创建出来,而是在运行的时候被创建出来
}

int main(void)
{
	test02();
	system("pause");
	return EXIT_SUCCESS;
}

08 类模板做函数的参数

1.指定传入类型

2.参数模板化

3.整体模板化

#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->m_Name = name;
		this->m_Age = age;
	}
	void showPerson()
	{
		cout << this->m_Name << "的年龄是" << this->m_Age << endl;
	}
	NameType m_Name;
	AgeType m_Age;
};

//1.指定传入类型
void doWork(Person<string, int>& p)
{
	p.showPerson();
}

void test01()
{
	Person<string, int>p("MT", 10);
	doWork(p);
}

//2.参数模板化
template<class T1, class T2>
void doWork2(Person<T1, T2>& p)
{
	//如何查看类型名称
	cout << "T1 的类型:" << typeid(T1).name() << endl;
	cout << "T2 的类型:" << typeid(T2).name() << endl;

	p.showPerson();
}

void test02()
{
	Person<string, int>p("狗贼", 10);
	doWork2(p);
}

//3.整体模板化
template<class T>
void doWork3(T& p)
{
	p.showPerson();
}

void test03()
{
	Person<string, int>p("戴比", 20);
	doWork3(p);
}

int main(void)
{
	test01();
	test02();
	test03();
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

09 类模板碰到继承的问题

child 继承于 base ,必须告诉base中的T的类型,否则T无法分配内存

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

template<class T>
class Base
{
public:

	T m_A;
};

//child 继承于 base ,必须告诉base中的T的类型,否则T无法分配内存
class Child :public Base<int>
{

};

//child2 也是模板类
template<class T1, class T2>
class Child2 :public Base<T2>
{
public:
	Child2()
	{
		cout << "T1的类型: " << typeid(T1).name() << endl;
		cout << "T2的类型: " << typeid(T2).name() << endl;
	}

public:

	T1 m_B;
};

void test01()
{
	Child2<int, double>child;//由用户指定类型
}

int main(void)
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

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

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

template<class T1, class T2>
class Person
{
public:

	Person(T1 name, T2 age);

	void showPerson();

	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{

	this->m_Name = name;
	this->m_Age = age;
}
template<class T1,class T2>
void Person<T1, T2>::showPerson()
{
	cout << "姓名:" << this->m_Name << "  "  << endl;
}

void test01()
{
	Person<string, int>p("孙悟空", 100);
	p.showPerson();
}

int main(void)
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

11 类模板的分文件编写问题以及解决

类模板的分文件编写问题以及解决:

.h 和 .cpp 分别写声明和实现
但是由于 类模板的成员函数运行阶段才去创建,导致包含.h头文件,不会创建函数的实现,无法解析外部命令
解决方案,保护.cpp文件(不推荐)
建议 模板不要做分文件编写,写到一个类中即可,类内进行声明和实现,最后把后缀名改为 .hpp

Person.hpp(头文件中):

#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);

	void showPerson();

	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{

	this->m_Name = name;
	this->m_Age = age;
}
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
	cout << "姓名:" << this->m_Name << "  " << endl;
}

类模板的分文件编写问题以及解决.cpp:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
#include "Person.hpp"

int main(void)
{
	Person<string, int>p("猪八戒",10);
	p.showPerson();

	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

12 类模板碰到友元函数-类内实现

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

template<class T1, class T2>
class Person
{
	friend void printPerson(Person<T1, T2>& p)
	{
		cout << "姓名:" << p.m_Name << "  年龄:" << p.m_Age << endl;
	}
public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	T1 m_Name;
	T2 m_Age;
};

void test01()
{
	Person<string, int>p("者行孙", 1500);
	printPerson(p);
}

int main(void)
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

13 类模板碰到友元函数-类外实现

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

//让编译器提前看到printPerson的声明
//让编译器看到Person类的声明
template<class T1, class T2>class Person;
template<class T1, class T2>void printPerson(Person<T1, T2>& p);

template<class T1, class T2>
class Person
{
	//利用空参数列表,告诉编译器 这是模板函数的声明,不加<>为普通函数
	friend void printPerson<>(Person<T1, T2>& p);//类里面的声明,需要编译器提前知道
	/*{
		cout << "姓名:" << p.m_Name << "  年龄:" << p.m_Age << endl;
	}*/
public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
void printPerson(Person<T1, T2>& p)
{
	cout << "姓名:" << p.m_Name << "  年龄:" << p.m_Age << endl;
}



void test01()
{
	Person<string, int>p("者行孙", 1500);
	printPerson(p);
}

int main(void)
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

14 类模板的应用-数组类封装

MyArray.hpp:

#pragma once
#include<iostream>
using namespace std;


template<class T>
class MyArray
{
public:
	//构造函数
	explicit MyArray(int capacity)  //防止隐式类型转换 防止MyArray arr = 10; 的写法
	{
		this->m_Capacity = capacity;
		this->m_Size = 0;
		this->pAddress = new T[m_Capacity];
	}

	MyArray(const MyArray& array)
	{
		this->m_Capacity = array.m_Capacity;
		this->m_Size = array.m_Size;
		this->pAddress = new T[array->m_Capacity];
		for (int i = 0; i < this->m_Size; ++i)
		{
			this->pAddress[i] = array.pAddress[i];
		}
	}

	~MyArray()
	{
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;
		}
	}

	//赋值操作符重载
	MyArray& operator=(const MyArray& array)
	{
		//先判断原始数据,有的话就清空
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;
		}
		this->m_Capacity = array.m_Capacity;
		this->m_Size = array.m_Size;
		this->pAddress = new T[array->m_Capacity];
		for (int i = 0; i < this->m_Size; ++i)
		{
			this->pAddress[i] = array.pAddress[i];
		}
	}

	//[]重载
	T& operator[](int index)
	{
		return this->pAddress[index];
	}

	//尾插法
	void push_Back(T val)
	{
		this->pAddress[this->m_Size] = val;
		this->m_Size++;
	}

	//获取大小
	int getSize()
	{
		return this->m_Size;
	}

	//获取容量
	int getCapacity()
	{
		return this->m_Capacity;
	}

private:
	T* pAddress;//指向堆区空间
	int m_Capacity;
	int m_Size;
};

类模板的应用-数组类封装.cpp:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include"MyArray.hpp"

//输出int类型的数组
void printIntArray(MyArray<int>& array)
{
	for (int i = 0; i < array.getSize(); ++i)
	{
		cout << array[i] << endl;
	}
}

int main(void)
{
	MyArray<int> arr(10);
	for (int i = 0; i < 10; ++i)
	{
		arr.push_Back(i * i);
	}
	printIntArray(arr);
	system("pause");
	return EXIT_SUCCESS;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值