Day 12 模板

一、函数模板:

1.什么是函数模板?

        把类型当做未知量(未知类型,可以是任何类型),可以忽略类型影响

例:求最大值函数(传入的参数可string可以是int)->体会模板的基本写法

template<typename _Ty1>//这里的typename可以写多个
_Ty1 Max(_Ty1 one, _Ty1 two)
{
	return one > two ? one : two;
}
void test1()
{
	cout << "较大的整数是" << Max(6, 9) << endl;
	cout << "较大的string是" << Max("python", "C++") << endl;
}

输出:

较大的整数是9
较大的string是python

注: ①.当然关键字typename在现在是可以等效于class的(写的更加方便)

②.实际上template和函数名这两行是一行,为了美观和可读性,笔者在此写作两行。

2.调用函数模板的两种方式:

①隐式调用:当做正常函数的调用即可。

②显式调用:

函数名<类型名>(参数)

如上述Max函数用的就是隐式调用,下面用的是显式调用。

cout << "较大的整数是" << Max<int>(6, 9) << endl;
cout << "较大的string是" << Max<const char*>("python", "C++") << endl;

3.函数模板的2种形态

        ①普通函数当做函数模板。(如:上面的Max函数)

        ②类的成员函数也可以写成函数模板(例(下)类中声明类外实现

class wbm
{
public:
	wbm(int age):age(age){}
	template<class _Ty1,class _Ty2>
	void print(_Ty1,_Ty2);
protected:
	int age;
};
template<class _Ty1, class _Ty2>
void wbm::print(_Ty1 a, _Ty2 b)
{
	cout << a << " " << b << endl;
}
void test2()
{
	wbm jie(1);
	jie.print("杰杰子",18);
}

输出:

杰杰子 18

注:函数模板的缺省,显示调用可以不用传类型,但是参数个数是不可以缺少的。

template<class _Ty1,class _Ty2=int>
void print(_Ty1 a, _Ty2 b)
{
	cout << a << " " << b << endl;
}
print<const char*>("杰杰子",18);//这边只需要写一个基本类型,第二个不写默认是int类型。

4、特殊写法:

        ①缺省写法(见上)

        ②存在常量写法。

template<class _Ty,size_t size=5>//size_t是unsigned long long类型
void printArray(_Ty array)
{
	for (int i = 0; i < size; i++)//在模板里有了size这个变量,下面函数直接用
	{
		cout << array[i] << "\t";
	}
}
void testarr()
{
	int arr[5] = { 1,2,5,3,66 };
	printArray<int*>(arr);//做了缺省  
	cout << endl;
	printArray<int*, 5>(arr);//等效
	cout << endl;
	printArray(arr);//隐式调用
}

输出:(注意:size_t是unsigned long long 的别称)

1       2       5       3       66
1       2       5       3       66
1       2       5       3       66

注:1.不能传入变量,只能传入常量,函数模板如果存在变量的情况下

        2.size=5做了缺省,可以隐式调用仅传一个参数arr;若不做缺省,必须显式调用,(来初始化size在尖括号中<int *, 11>)

二、类模板

1.如何生成一个类模板?

template<class _Ty>class wbm{};

(注:用没用到_Ty只要template修饰就是一个类模板)

 2.调用:

        ①必显示调用

        ②类名不是一个实际类型=>所有用到类名的地方都需要使用如下的形式去用:

类名<未知类型>

实例1:

template<class _Ty>
class jie
{
public:
	jie(_Ty a):num(a){}
	void print();
protected:
	_Ty num;
};
template <class _Ty>
void jie<_Ty>::print()
{
	cout << num << endl;
}
template<class _Ty>
class wbm :public jie<_Ty>
{
public:
	wbm(int age):jie<_Ty>(age){}
protected:
	int age;
};
void test1()
{
	jie<int> zi(2);//用到了模板类,都需要写<类型>
	zi.print();
	wbm<double> bao(20);
	bao.print();
}

注意:1.子类继承父类的时候,需要前置template <class _Ty> 此后再写尖括号的时候就无需再写class

          2.类模板的类中声明,类外实现的函数也需要前置template <class _Ty> 此后再写尖括号的时候就无需再写class (class 一般只在template的时候加上

           3.public 父类<> 用到父类这个模板类均需要把尖括号带着。包括子类和父类在创建对象的时候,均不能忘记<>尖括号。以及在子类的构造函数中调用父类的构造函数的时候,都需要加上尖括号<>

实例2:

//案例2
template <class _Ty1,class _Ty2>
class Date
{
public:
	Date(_Ty1 a,_Ty2 b):a(a),b(b){}
	void print();
protected:
	_Ty1 a;
	_Ty2 b;
};
template <class _Ty1, class _Ty2>//类外实现print函数
void Date<_Ty1,_Ty2>::print()
{
	cout << a << "\t" << b << endl;
}
void test2()
{
	Date<const char*,int> name_age("guozijie", 18);
	Date<int, int > score(100, 100);
	name_age.print();
	cout << "高数" << "\t" << "线代" << endl;
	score.print();
}

输出:

guozijie        18
高数    线代
100     100

注意:多文件中,类模板 中的声明和实现一定在一起的,不能分开写  

三、自定义类型当做模板的参数

1.基本的自定义类型当做参数:

案例1:模板函数print的参数传入一个对象并打印出其中的数据。

//案例一:模板函数print的参数传入一个对象并打印出其中的数据。
class wbm
{
public:
	wbm(string name, int age):name(name),age(age){}
	friend ostream& operator<<(ostream& out, wbm& b)
	{
		out << b.name << "\t" << b.age;
		return out;
	}
protected:
	string name;
	int age;
};
template<class _Ty>
void print(_Ty a)
{
	cout << a << endl;
}
void testprint()
{
	print(wbm("二狗", 19));
	print(666);
}

案例2: //案例2:写一个B类,比较B1,B2中成员数据较大的那个

class B
{
public:
	B(int age):age(age){}
	operator int()
	{
		return age;
	}
protected:
	int age;
};
template<class ty1,class ty2>
void testComAge(ty1 a,ty2 c)
{
	if (a > c)
	{
		cout << "前者年龄较大" << endl;
	}
	else
	{
		cout << "后者年龄较大" << endl;
	}
}

2.当自定义类型也是一个模板

案例:写一个链表,实现各种数据类型的存储。

class bm
{
public:
	bm(string name,int age):name(name),age(age){}
	friend ostream& operator<<(ostream& out, bm& bb)
	{
		out << bb.name << "\t" << bb.age << endl;
		return out;
	}
protected:
	string name;
	int age;
};
template<class _ty>
class Node
{
public:
	Node(_ty data, Node<_ty>* next):data(data),next(next){}
	_ty& getdata()
	{
		return data;
	}
	Node<_ty>* getnext()
	{
		return next;
	}
protected:
	_ty data;
	Node<_ty>* next;
};
template<class _ty>
class List
{
public:
	List()
	{
		headNode = nullptr;//创建一个空链表
	}
	void insertNode(_ty data)
	{
		headNode = new Node<_ty>(data, headNode);//前插法,only one sentence
	}//别忘记Node<>  尖括号写到这容易忘。
	void printList()
	{
		Node<_ty>*pMove = headNode;//定位到开头准备从头开始遍历
		while (pMove!=nullptr)
		{
			cout << pMove->getdata();//pMove也是Node*类型
			pMove = pMove->getnext();
		}//此处还要注意一下访问权限问题,需要调用公有接口!!!
	}
protected:
	Node<_ty> *headNode;//注意:都是Node*<>类型,缺一不可
};
void testList()
{
	List<double> list;
	list.insertNode(283.2);
	List<bm> blist;
	blist.insertNode(bm("guo", 12));
	blist.insertNode(bm("zi", 15));
	blist.insertNode(bm("jie", 18));
	blist.printList();
}

注意:尖括号不能忘,是Node*类型还是Node类型要分清楚,访问权限要给一个接口。

优化:利用using语法,可以这样写

using Lptype=Node<_ty>;

那么下面的Node* headNode等都可以简化为:

Lptype * headNode;

思考 :

//error C2679: 二进制“<<”: 没有找到接受“_Ty”类型的右操作数的运算符(或没有可接受的转换)

原因: 忘记了加上引用符号。去给data去取别名再调用。!!!!!

 

四、模板嵌套

关键:明白类型是什么。

案例一:函数模板参数用模板类,分别隐式调用、显式调用

案例二:一个Data类模板调用另一个类的n个对象作为数据成员并打印。

//案例一:函数模板参数用模板类,再写一个print函数分别隐式调用和显式调用
template<class _ty1,class _ty2>
class wbm
{
public:
	wbm(_ty1 one,_ty2 two):one(one),two(two){}
	friend ostream& operator<<(ostream& out, wbm& bb)
	{
		out << bb.one << "\t" <<bb.two<<endl;
		return out;
	}
protected:
	_ty1 one;
	_ty2 two;
};
template<class _ty,class _ty2>
class Data
{
public:
	Data(_ty a,_ty2 b):a(a),b(b) {}
	void printdata()
	{
		cout << a << "   " << b;
	}
protected:
	_ty a;
	_ty2 b;
};
void testFunc()
{
	Data<wbm<string, int>, wbm<int, int>>
		data(wbm<string, int>("杰杰子", 18), wbm<int, int>(100, 100));
	data.printdata();
}
template<class _ty>
void print(_ty one)
{
	cout << one;
}
int main()
{
	testFunc();
	//隐式调用print
	print(wbm<string, int>("jie", 181));
	//显式调用print
	print<wbm<string, int>>(wbm<string, int>("jie", 182));
	//using语法简化代码
	using wbmty1 = wbm<string, int>;
	print<wbmty1>(wbmty1("jie", 183));
	return 0;
}

五、模板重载(函数)

函数名相同的普通函数,模板函数的重载(显式必调用模板

参数类型与普通函数一致,则优先调用普通函数注:字符串类型优先推导为const char* 类型

两个模板同时成立是,优先调用类型相似度高的那个

#include <iostream>
using namespace std;

void print(int a, string b)
{
	cout << "普通函数" << endl;
}
template <class _Ty1, class _Ty2>
void print(_Ty1 a, _Ty2 b)
{
	cout << "两个类型" << endl;
}

template <class _Ty>
void print(_Ty a, _Ty b)
{
	cout << "一个类型" << endl;
}

int main()
{
	print<int, string>(12, "显示调用百分调用模板");
	print(12, string("优先调用适应的普通函数"));
	//两个模板同时成立,优先调用类型相似度高的那个
	print(12, 12); // 
	return 0;
}

输出:

两个类型
普通函数
一个类型

六、类模板的特化:

1.局部特化:特殊化,重写产生的类,类名要用: 类名<类型> 方式使用。

(对于相同的2个基本数据类型,有特殊情况\不兼容的时候,需要这样写)

例案:A类和Data类,A两个参数均为Data类,最好调用print()函数,而不是cout<<(需要重载)

//两未知类型
template <class _Ty1,class _Ty2>
class MM 
{
public:
	MM(_Ty1 one, _Ty2 two) :one(one), two(two) {}
	void print() 
	{
		cout << one << " " << two << endl;
	}
protected:
	_Ty1 one;
	_Ty2 two;
};
class Data 
{
public:
	Data(int a, int b) :a(a), b(b) {}
	void print() 
	{
		cout << a << " " << b << endl;
	}
protected:
	int a;
	int b;
};

//局部特化,特殊化
template <class _Ty>
class MM<_Ty,_Ty>     //特化产生类,类名要用: 类名<类型> 方式使用
{
public:
	MM(_Ty one, _Ty two) :one(one), two(two) {}
	void print()
	{
		//cout << one << " " << two << endl;
		one.print();
		two.print();
		cout << "特殊化" << endl;
	}
protected:
	_Ty one;
	_Ty two;
};

2.完全特化:template<> class wbm(string,string){   };已经没有未知类型了

//完全特化
template <>
class MM<string, string> 
{
public:
	MM(string one, string two) :one(one), two(two) {}
	void print()
	{
		cout << "完全特化" << endl;
		cout << one << " " << two << endl;
	}
protected:
	string one;
	string two;
};

int main() 
{
	//原生模板
	MM<string, int> mm1("小芳", 18);
	mm1.print();

	//局部特化模板
	MM<Data, Data> dMM(Data(1,2),Data(3,4));
	dMM.print();

	//完全特化的模板
	//折叠参数 后续讲元组容器的时候讲
	MM<string, string>  mm2("小丽", "小美");
	mm2.print();

	tuple<string, string> tp("张三", "李四");

	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Ocean__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值