24、模板

泛型编程:主要利用的技术就是模板

模板:函数模板类模板

模板: 提高复用性,将类型参数化

二、函数模板

1、函数模板语法

template<typename T>

template: 声明创建模板

typename: 后面符号是 数据类型(可用class代替)

T:通用的数据类型

//模板
template<typename T>
void mySwap(T& a, T& b)
{

    T temp = a;
    a = b;
    b = temp;
}

int a = 10;
int b = 20;

利用模板交换

1.自动类型推导

mySwap(a, b);

2.显示指定类型

mySwap<int>(a, b);

#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>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

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

	//swapInt(a, b);

	//利用模板交换
	//1.自动类型推导
	mySwap(a, b);

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

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


	double c = 1.1;
	double d = 2.2;

	//mySwap(c, d);

	mySwap<double>(c, d);

	//swapDouble(c, d);

	cout << "c=" << c << endl;
	cout << "d=" << d << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

2、函数模板注意事项

(1)、自动推导类型必须导出一致的类型T才可以使用

(2)、模板必须却顶出T的类型,才可以使用

#include<iostream>
using namespace std;

template<typename T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

//自动推导类型,必须推导出一致的数据类型,才能使用
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';

	mySwap(a, b);

	//mySwap(a, c);   //错误

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

//2、模板必须却顶出T的类型,才可以使用
template<typename T>
void func()
{
	cout << "func()" << endl;
}

void test02()
{
	//func();   //出错
	func<int>();	//正确
}

int main()
{
	test01();

	system("pause");
	return 0;
}

3、函数模板的案例

#include<iostream>
using namespace std;

//两个数交换的模板
template<typename T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

//实现一个通用的,对数组进行排序的函数
template<typename 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 (i != max)
		{
			mySwap(arr[i], arr[max]);
		}
	}
}

//打印数组的模板
template<typename T>
void printArray(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

//测试char类型数据排序
void test01()
{
	char charArray[] = "badcf";

	char a = 'a';
	char b = 'b';

	int length = sizeof(charArray) / sizeof(char);

	printArray(charArray, length);

	mySort(charArray, length);

	printArray(charArray, length);

	//cout << (a > b ? a : b) << endl;    // h
}


//测试整数排序
void test02()
{
	int intArray[] = { 3,2,8,1,5,9,4,7 };

	int length = sizeof(intArray) / sizeof(int);

	printArray(intArray, length);

	mySort(intArray, length);

	printArray(intArray, length);
}


int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}

4、普通函数和函数模板的区别

int a=10;

int b=20;

char c='c';

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

cout << myAdd(a, b) << endl;    //(30)

cout << myAdd(a, c) << endl;    //(109)

2、函数模板调用时:

(1).如果利用自动类型推导不会发生隐式类型转换

cout << myAdd01(a, c) << endl;   //出错   //如果利用自动类型推导,不会发生隐式类型转换

(2).如果利用显示指定类型的方法,可以发生隐式类型转换

 cout << myAdd01<int>(a, c) << endl;   //√    //(109)

#include<iostream>
using namespace std;

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

//普通函数
int myAdd(int a, int b)
{
	return a + b;
}

//函数模板
template<typename T>
T myAdd01(T a, T b)
{
	return a + b;
}

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

	//普通函数
	cout << myAdd(a, b) << endl;
	cout << myAdd(a, c) << endl; //普通函数调用时,可以发生自动类型转换(隐式类型转换)

	//模板
	cout << myAdd01(a, b) << endl;
	//cout << myAdd01(a, c) << endl;   //出错  
							     	   //1.函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
	cout << myAdd01<int>(a, c) << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

5、普通函数和函数模板调用规则

(1)、如果普通函数和函数模板都可以实现,优先调用普通函数

(2)、可以通过空参数列表,来强制调用函数模板

(3)、函数模板也可以发生重载

(4、)如果函数模板可以产生更好的匹配,优先调用函数模板

 (总结: 既然提供了函数模板,就不要使用提供的普通函数,否则容易出现二义性)

#include<iostream>
using namespace std;

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

//光定义不实现,不会出错,只要不调用就行
void func();

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()
{
	int a = 10;
	int b = 20;

	//1.如果普通函数和函数模板都可以实现,优先调用普通函数
	myPrint(a, b);   //调用普通函数

	//2.可以通过空参数列表,来强制调用函数模板
	myPrint<>(a, b);  //调用函数模板

	//myPrint<int>(a, b);  //调用函数模板

	int c = 30;
	myPrint(a, b, c);  //3.调用重载的函数模板

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

int main()
{
	test01();


	system("pause");
	return 0;
}

6、模板的局限性

碰到自定义数据类型,无法处理

解决办法:利用具体化(自定义数据类型)的模板,优先调用

(学习模板并不是为了写模板,而是在STL中,能够运用系统提供的模板)

template<typename T>
bool myCompare(T& a, T& b)
{

    if (a == b)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//利用具体化Person的版本,实现代码,具体优化调用
template<> bool myCompare(Person &p1, Person &p2)
{

    if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
    {
        return true;
    }
    else
    {
        return false;
    }
} 

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

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

//特定的数据类型,需要具体化的方式做特殊处理
template<typename T>
bool myCompare(T& a, T& b)
{
	if (a == b)
	{
		return true;
	}
	else
	{
		return false;
	}
}

//利用具体化Person的版本,实现代码,具体优化调用
template<> bool myCompare(Person &p1, Person &p2)
{
	if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
	{
		return true;
	}
	else
	{
		return false;
	}
}

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

	bool ret = myCompare(a, b);

	if (ret)
	{
		cout << "a==b" << endl;
	}
	else
	{
		cout << "a!=b" << endl;
	}
}

void test02()
{
	Person p1("张三", 14);
	Person p2("李四", 15);

	bool ret = myCompare(p1, p2);

	if (ret)
	{
		cout << "p1 和 p2 姓名年龄相等!" << endl;
	}
	else
	{
		cout << "p1 和 p2 姓名年龄不相等!" << endl;
	}
}

int main()
{
	//test01();

	test02();

	system("pause");
	return 0;
}

三、类模板

1、类模板的具体语法

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

//类模板
template<class NameType,class AgeType>
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	void shoePerson()
	{
		cout << "name:" << this->m_Name << "  age:" << this->m_Age << endl;
	}
public:
	NameType m_Name;
	AgeType m_Age;
};

void test01()
{
	Person<string, int> p1("孙悟空", 999);

	p1.shoePerson();
}

int main()
{
	test01();

	system("pause");
	return 0;
}

2、类模板和函数模板的区别

(1)、类模板没有自动类型推导

Person p1("孙悟空", 999);   //×

Person<string,int> p2("猪八戒", 888);   //

(2)、类模板在模板参数类型中可以有默认值

template<class NameType,class AgeType = int>
 class Person
 {

 public:
            .............
 };

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

//类模板
 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 << "name:" << this->m_Name << "   age:" << this->m_Age << endl;
	 }
 public:
	 NameType m_Name;
	 AgeType m_Age;
 };

 void test01()
 {
	 //1、类模板无法使用自动类型推导
	 //Person p1("孙悟空", 999);   //错误

	 Person<string,int> p2("猪八戒", 888);
	 p2.showPerson();
 }

 void test02()
 {
	 //类模板参数列表,可以有默认参数
	 Person<string> p2("沙和尚", 878);
	 p2.showPerson();
 }

int main()
{
	//test01();

	test02();

	system("pause");
	return 0;
}

3、类模板中函数模板的创建时机

普通类中的成员函数,一开始就可以创建

类模板中成员函数,在调用时才可以创建

#include<iostream>
using namespace std;

class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 show" << endl;
	}
};

class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2 show" << endl;
	}
};


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

	void func2()
	{
		obj.showPerson2();
	}
public:
	T obj;  //Person1 m;
};



void test01()
{
	myClass<Person1> m;

	m.func1();

	//类模板中,成员函数,使用时才调用
	//p1.func2();   //出错,不调用,不创建。一调用创建,发现错误
}

//void test02()
//{
//	Person1 p1;
//	p1.showPerson1();
//}

int main()
{
	test01();
	//test02();

	system("pause");
	return 0;
}

4、类模板对象做函数参数

类模板实例化出的参数,向函数传参---自动类型推导,显示指定类型

三种传入方式

(1)、指定传入的类型:直接显示对象的数据类型(比较常用)

(2)、参数模板化:将对象中的参数直接变为模板进行传递

(3)、整个类模板化:将这个对象模型模板化进行传递

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

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


//1.指定传入的类型:直接显示对象的数据类型
void printPerson1(Person<string,int> &p)
{
	p.showPerson();
}

void test01()
{
	Person<string, int> p("孙悟空", 999);
	printPerson1(p);
}


//参数模板化:将对象中的参数直接变为模板进行传递
template<class T1,class T2>
void printPerson(Person<T1, T2>& p)
{
	p.showPerson();
	cout << typeid(T1).name() << endl;   //string...
	cout << typeid(T2).name() << endl;   //int 
}

void test02()
{
	Person<string, int> p("猪八戒", 888);
	printPerson(p);
}

//整个类模板化:将这个对象模型模板化进行传递
template<class T>
void printPerson3(T& p)
{
	p.showPerson();
	cout << typeid(T).name();
}

void test03()
{
	Person<string, int> p3("沙僧", 878);
	printPerson3(p3);
}

int main()
{
	//test01();
	//test02();
	test03();

	system("pause");
	return 0;
}

5、类模板与继承

(1)、当子类继承的父类是一个类模板时,子类在声明的时候,必须要指定出父类中T的类型

        如果不指定,编译器无法给子类分配内存。

(2)、如果想灵活指定出父类中T的类型子类也变为类模板

#include<iostream>
using namespace std;

//父类是一个类模板
template<class T>
class Base
{
public:
	T m;
};

//1.父类是类模板,子类必须指定父类T类型,才能使用
class Son :public Base<int>   
{

};

void test01()
{
	Son s1;
	cout << sizeof(s1) << endl;
}


//2.想灵活指定父类中的类型,子类也要声明为模板
template<class T1,class T2>
class Son2 :public Base<T2>
{
public:
	Son2()
	{
		cout << "T1 的类型:" << typeid(T1).name() << endl;  //int
		cout << "T2 的类型:" << typeid(T2).name() << endl;  //char
	}
public:
	T1 obj;
};

void test02()
{
	Son2<int, char> s2;
}



int main()
{
	//test01();

	test02();

	system("pause");
	return 0;
}

6、类模板成员函数类外实现

类模板在类外实现时,需要加上模板的参数列表

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

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

	void showPerson();
public: 
	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 << "name:" << this->m_Name << "  age:" << this->m_Age << endl;
}



void test01()
{
	Person<string, int> p1("张三", 23);
	p1.showPerson();
}

int main()
{
	test01();

	system("pause");
	return 0;
}

7、类模板分文件编写

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

(1)、第一种解决方法,直接包含源文件
#include"person.cpp"

(2)、第二种解决方法,将.h和.cpp文件写在一起,后缀名该为.hpp文件
#include"person.hpp"

(主流的方法时第二种)

————————————————

main.cpp 

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


//类模板的分文件编写问题
// 
//第一种解决方法,直接包含源文件
//#include"person.cpp"

//第二种解决方法,将.h和.cpp文件写在一起,后缀名该为.hpp文件
#include"person.hpp"



void test01()
{
	Person<string, int> p1("Jerry", 23);
	p1.showPerson();
}

int main()
{
	test01();

	system("pause");
	return 0;
}

Person.hpp

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

template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age);
	void showPerson();
public:
	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 << "name: " << this->m_Name << endl;
	cout << "age:" << this->m_Age << endl;
}

8、类模板与友元

全局函数做友元;

(1)、类内实现(简单)

(2)、类外实现(复杂)

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

___________全局函数类外实现_______________

template<class T1,class T2>

class Person;

-----------------------------------------

template<class T1,class T2>

void printPerson2(Person<T1, T2> p)

{

        cout<<"....................."<<endl;

}

--------------------------------------------

template<class T1,class T2>

class Person

{

        friend printPerson2<>(Perosn<T1,T2> p);

        .......

}

#include<iostream>
using namespace std;

//全局函数类外实现,先让编译器知道有这样的函数存在,
//看到参数Person,还要让编译器知道Person是一个类模板
template<class T1,class T2>
class Person;

template<class T1, class T2>
void printPerson2(Person<T1, T2> p)
{
	cout << "全局函数类外实现--name:" << p.m_Name << "    age:" << p.m_Age << endl;
}


//类模板
template<class T1,class T2>
class Person
{
public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	//全局函数类内实现
	friend void printPerson(Person < T1, T2> p)
	{
		cout << "name:" << p.m_Name << "   age:" << p.m_Age << endl;
	}
	//全局函数类外实现,printPerson2<>,加<>让编译器知道是一个模板
	friend void printPerson2<>(Person<T1, T2> p);

private:
	T1 m_Name;
	T2 m_Age;
};

//全局函数,类外实现,出错
//template<class T1,class T2>
//void printPerson2(Person<T1, T2> p)
//{
//	cout << "全局函数类外实现--name:" << p.m_Name << "    age:" << p.m_Age << endl;
//}


void test01()
{
	Person<string, int> p1("范小花", 23);

	printPerson(p1);
}

void test02()
{
	Person<string, int> p2("Jeffy", 19);

	printPerson2(p2);
}

int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}

9、类模板案例-数组类封装

模板实现一个数组,可以用内置数据类型,也可以用自定义数据类型

——————————————

main.cpp

#include<iostream>
using namespace std;
#include"myArray.hpp"

void printArray(myArray<int> arr)
{
	for (int i = 0; i < arr.get_Size(); i++)
	{
		cout << arr[i] << "  ";
	}
	cout << endl;
}

void test01()
{
	myArray<int> arr1(5);    //5是容量,不是大小

	cout << "arr1.get_Capacity() = " << arr1.get_Capacity() << endl;  
	cout << "arr1.get_Size() = " <<arr1.get_Size() << endl;   

	for (int i = 0; i < 5; i++)
	{
		arr1.push_Back(i);
	}
	printArray(arr1);

	arr1.pop_Back();
	cout << "arr1.get_Size() = " << arr1.get_Size() << endl;  
	
	printArray(arr1);
	cout << "arr1.get_Size() = " << arr1.get_Size() << endl;  


	//cout<<arr1[3];   //出错,arr1是对象,无法通过下标访问

	//arr1[3] = 19;

	//arr2 = arr1;
}

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

void printPersonArray(myArray<Person> arr)
{
	for (int i = 0; i < arr.get_Size(); i++)
	{
		cout << "姓名:" << arr[i].m_Name << "\t年龄:" << arr[i].m_Age << endl;
	}
	cout << endl;
}

void test02()
{
	myArray<Person> arr(10);

	Person p1("孙悟空", 999);
	Person p2("猪八戒", 888);
	Person p3("沙和尚", 878);
	Person p4("唐僧", 21);

	arr.push_Back(p1);
	arr.push_Back(p2);
	arr.push_Back(p3);
	arr.push_Back(p4);

	printPersonArray(arr);

	cout << "arr.get_Capacity()=" << arr.get_Capacity() << endl;
	cout << "arr.get_Size()=" << arr.get_Size() << endl;
}


int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}

myArray.hpp

//字节通用的数字类
#pragma once
#include<iostream>
using namespace std;

template<class T>
class myArray
{
public:
	//构造函数
	myArray(int capacity)
	{
		this->m_Capacity = capacity;
		this->m_Size = 0;
		this->pAddress = new T[this->m_Capacity];
	}

	//拷贝构造函数
	myArray(const myArray& arr)
	{
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;

		//深拷贝,重新在堆区创建内容
		this->pAddress = new T[arr.m_Capacity];   

		//将arr中的元素全部拷贝过来
		for (int i = 0; i < this->m_Size; i++)
		{
			this->pAddress[i] = arr.pAddress[i];
		}

		//arr.pAddress = new T[this->Capacity];   //×
		//arr.m_Capacity = this->m_Capacity;
		//arr.m_Size = this->m_Size;
	}

	//重载operrator,防止浅拷贝
	myArray& operator=(const myArray& arr)   //返回引用,返回本身
	{
		//此对象以前可能创建的时候,调用构造函数,已经在堆区开辟空间,要先释放以前在堆区开辟的内存
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;
		}

		this->pAddress = new T[this->m_Capacity];

		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		
		for (int i = 0; i < this->m_Size; i++)
		{
			this->pAddress[i] = arr.pAddress[i];
		}

		return *this;
	}

	//析构函数
	~myArray()
	{
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;   //delete的是指针,指针指向堆区开辟的内存
			this->pAddress = NULL;
		}
	}

	//尾插法
	void push_Back(const T& val)
	{
		//判断容量是否已满
		if (this->m_Capacity == this->m_Size)
		{
			cout << "数组容量已满" << endl;
		}
		else
		{
			this->pAddress[this->m_Size] = val;
			this->m_Size++;
		}
	}

	//尾删法
	void pop_Back()
	{
		if (this->m_Size == 0)
		{
			cout << "数组中没有元素可以删除了" << endl;
		}
		else
		{
			this->m_Size--;
		}
	}

	//通过下标访问数组中的元素
	T& operator[](int index)
	{
		for (int i = 0; i < this->m_Size; i++)
		{
			if (index == i)
			{
				return this->pAddress[i];
			}
		}
	}

	//返回数组容量
	int get_Capacity()
	{
		return this->m_Capacity;
	}

	//返回数组大小
	int get_Size()
	{
		return this->m_Size;
	}

private:
	T* pAddress;       //指向堆区真实数组的指针
	int m_Capacity;  //数组容量
	int m_Size;      //数组大小
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值