C++学习——函数模板、类模板

C++泛型编程:用模板技术实现【参数类型化】

函数模板的基本使用方法

template<class T> 或  template<typename T>  告诉编译器紧跟的代码里出现T不要报错

例子:交换两个数字【如果按照常规的写法,那一种数据类型就要写一个函数,太麻烦】

void mySwapInt(int &a, int &b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

void mySwapDouble(double &a, double &b)
{
	double tmp = a;
	a = b;
	b = tmp;
}

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

	double c = 3.14;
	double d = 1.1;
	mySwapDouble(c, d);
}

解决方法:使用类型参数化,也就是泛型编程——模板技术。

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

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

	mySwap(a, b);      //1、自动类型转换,必须有参数类型才可以推导
	mySwap<int>(a, b); //2、显示指定类型
	//mySwap(a, c1);   推导不出来T, 所以不能运行

	double c = 3.14;
	double d = 1.1;
	mySwap(c, d);
    mySwap<double>(a, b);
}

写一个数组排序的例子

//使用函数模板实现对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 printArray(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

void test01()
{
	char charArr[] = "helloworld";
	int len = strlen(charArr);  // ==sizeof(charArr)/size(char)
	mySort(charArr, len);
	printArray(charArr, len);

	int intArr[] = { 1, 4, 0, 100 };
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

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

 C++编译器优先考虑普通函数

//1、编译器优先考虑普通函数
//函数模板:不可以进行隐式类型转换
template<class T>
T myPlus(T a, T b)
{
	return a + b;
}
//普通函数: 可以进行隐式类型转换
int myPlus2(int a, int b)
{
	return a + b;
}

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

	char c = 'c';
	//myPlus(a, c); //类型推导不出来
	myPlus2(a, c);  //可以,输出的结果是109 = 10 + 99
}

可以通过空模板实参列表的语法限定编译器只能通过模板匹配

如果想强制调用模板,那么可以使用空参数列表myPrint<>(m,n);

//2、可以通过空模板实参列表的语法限定编译器只能通过模板匹配
//函数模板
template<class T>
void myPrint(T a, T b)
{
	cout << "模板函数调用的myPrint" << endl;
}

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

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

void test01()
{
	int m = 10;
	int n = 20;
	myPrint(m, n); //如果出现重载,优先使用普通函数调用,如果普通函数没有实现,就会出现错误

	//如果想强制调用模板,那么可以使用空参数列表
	myPrint<>(m, n);
}

函数模板可以像普通函数那样可以被重载

//3、函数模板可以发生重载
template<class T>
void myPrint(T a, T b)
{
	cout << "模板函数调用的myPrint" << endl;
}

template<class T>
void myPrint(T a, T b, T c)  //函数模板可以发生重载
{
	cout << "abc模板函数调用的myPrint" << endl;
}

void test01()
{
	int m = 10;
	int n = 20;
	int k = 40;
	myPrint(m, n, k);
}

如果函数模板可以产生一个更好的匹配,那么选择模板

//4、如果函数模板可以产生更好的匹配,那么优先调用函数模板
//函数模板
template<class T>
void myPrint(T a, T b)
{
	cout << "模板函数调用的myPrint" << endl;
}

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

void test01()
{
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2);  //此时调用函数模板
}

模板机制

编译器并不是把函数模板处理成能够处理任何类型的函数:模板并不是万能的,不能通用所有的数据类型

函数模板通过具体类型产生不同的函数:通过模板生成的函数,叫模板函数

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

编译器对函数模板进行两次编译,第一次对模板进行编译,第二次对替换T类型后的代码进行编译

模板的局限性及解决

模板的局限性:模板不能解决所有的类型

如果出现了不能解决的类型,可以通过第三代具体化来解决问题:template<>返回值 函数名<具体类型>(参数)

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

	string m_Name;
	int m_Age;
};

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

//第三代具体化
template<>bool myCompare<Person>(Person &a, Person &b)
{
	if (a.m_Name == b.m_Name && a.m_Age == b.m_Age)
	{
		return true;
	}
	return false;
}

void test01()
{
	int a = 10;
	int b = 20;
	int ret = myCompare(a, b);
	cout << "ret = " << ret << endl;

	Person p1("Tom", 10);
	Person p2("Jerry", 20);
	int ret = myCompare(p1, p2); //如果只用普通的模板会发生错误,需要进行第三代具体化
}

类模板的基本使用

语法:template<class NameType, class AgeType> 紧跟着类

与函数模板的区别,可以有默认类型参数  template<class NameType, class AgeType = int>

函数模板可以进行自动类型推到,而类模板不行

//template<class NameType, class AgeType = int> // 类模板可以有默认的参数
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 << endl;
		cout << "年龄:" << this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;
};

void test01()
{
	//自动类型推到, 类模板,不支持
	//Person p1("张三", 100);
	//只能用显示指定类型
	Person<string, int> p("张三", 100);
	p.showPerson();
}

类模板  的成员函数 一开始不会创建出来,而是在运行时才创建

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 test01()
{
	MyClass<Person1> m;
	m.func1();
	//m.func2();  //Person2不是Person1的成员
}

类模板做函数的参数

指定传入类型

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

参数模板化

template<class T1, class T2>
void doWork2(Person<T1, T2> &p)
{
	//如何查看类型
	cout << typeid(T1).name() << endl;
	cout << typeid(T2).name() << endl;
	p.showPerson();
}

整体模板化:对Person这个对象进行了整体模板化

template<class T>
void doWork3(T &p)
{
	cout << typeid(T).name() << endl;
	p.showPerson();
}

类模板遇到继承的问题

基类如果是模板类,必须让子类告诉编译器,基类中的T到底是什类型class Child :public Base<int>。如果不告诉,那么无法分配内存,编译不过

template<class T>
class Base
{
public:
	T m_A;
};

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

};

template<class T1, class T2>
class Child2 :public Base<T2>
{
public:
	Child2()
	{
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
	}
	T1 m_B;
};


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

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

template<class T1,class T2>
class Person
{
public:
	//类内实现
	/*Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}*/
	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 << "的年龄是" << this->m_Age << endl;
}

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

由于 类模板的成员函数只有在运行阶段才去创建,导致包含.h头文件,不会创建函数的实现,无法解析外部命令。

建议类模板不要做分文件编写,一般写到一个文件.h中,并把文件的后缀名改成.hpp

友元函数碰到类模板

友元函数类内实现

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("张三", 10);
	printPerson(p);
}

友元函数类外实现

//让编译器提前看到Person类声明
template<class T1, class T2> class Person;
//让编译器提前看到printPerson声明
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);  普通函数的声明
	friend void printPerson<>(Person<T1, T2> &p);  //利用空参数列表 告诉编译器 模板函数的声明
	
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("张三", 10);
	printPerson(p);
}

类模板的封装——数组类的构造

#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[this->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 < m_Size; i++)
		{
			this->pAddress[i] = array[i];
		}
	}

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

	MyArray& operator=(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 < m_Size; i++)
		{
			this->pAddress[i] = array[i];
		}
	}

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

	void pushBack(T val)   //值传递
	{
		this->pAddress[this->m_Size] = val;
		this->m_Size++;
	}

	int getSize()
	{
		return this->m_Size;
	}

	int getCapacity()
	{
		return this->getCapacity;
	}


private:
	T *pAddress;    //指向堆区指针
	int m_Capacity;
	int m_Size;
};
#include <iostream>
#include <string>
#include "MyArray.hpp"
using namespace std;

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

class Person
{
public:
	Person()
	{

	}

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

void printPersonArray(MyArray<Person>& array)
{
	for (int i = 0; i < array.getSize(); i++)
	{
		cout << "姓名:" << array[i].m_Name << "年龄:" << array[i].m_Age << endl;
	}
}

void test01()
{
	MyArray<int> arr(10);
	for (int i = 0; i < 10; i++)
	{
		arr.pushBack(i+100);
	}
	printIntArray(arr);

	Person p1("Tom", 10);
	Person p2("Herry", 11);
	Person p3("Jack", 12);
	Person p4("Hebe", 13);
	Person p5("Tim", 14);
	MyArray<Person> arr2(5);
	arr2.pushBack(p1);
	arr2.pushBack(p2);
	arr2.pushBack(p3);
	arr2.pushBack(p4);
	arr2.pushBack(p5);
	printPersonArray(arr2);
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值