C++学习笔记(11)

目录

一.函数模板

1.定义和声明

2.调用函数模版

3.函数模版在类中的应用

4.函数模板重载

二.类模板

1.声明语法

2.类模板的声明,实现和调用

3.模板类继承

4.自定义类型当模板参数

三.模板嵌套

1.函数模板嵌套

2.类模板嵌套

四.类模板特化

1.局部特化

2.完全特化 


一.函数模板

什么是模板?简单来说,就是一段任何类型都通用的代码,比如我们定义一个比较大小的函数,那这个函数可以适合整型数据,也可以适合浮点型,甚至可以适合string类型,这种把类型当作未知量,可以忽略类型影响的类或函数,我们称之为模版。先来了解函数模版。

1.定义和声明

如何声明和定义一个函数模版?下面以一个比较大小的函数为例:

template <typename _Ty>
_Ty Max(_Ty a, _Ty b)
{
	return a > b ? a : b;
}

也可以同时定义多个未知类型:

template <typename _Ty1, typename _Ty2>
void print(_Ty1 a, _Ty2 b)
{
	cout << a << " " << b;
}

如果觉得typename太陌生的话,也可以将它换成class,这也是可以的。

template <class C>
void printC(C c)
{
	cout << c;
}

2.调用函数模版

调用函数模版有隐式调用和显式调用两种,先来看隐式调用:

template <typename _Ty>
_Ty Max(_Ty a, _Ty b)
{
	return a > b ? a : b;
}
int main()
{
	cout << Max(2, 6) << " " << Max(2.1, 2.2);
}

可以看到,隐式调用跟正常的函数传参没什么区别,它会自己识别你的类型,再来看看显式调用:

template <typename _Ty>
_Ty Max(_Ty a, _Ty b)
{
	return a > b ? a : b;
}
int main()
{
	cout << Max<int>(2, 6) << " " << Max<double>(2.1, 2.2);
}

可以看到,显示调用的格式是函数名<类型名>(参数)。同样,多个未知类型的调用也是如此:

template <typename _Ty1, typename _Ty2>
void print(_Ty1 a, _Ty2 b)
{
	cout << a<< " " << b;
}
int main()
{
	print(1, 1.2);
	print<int, double>(1, 2.1);
}

3.函数模版在类中的应用

class A
{
public:
	template <class _Ty1>
	void print(_Ty1 a)
	{
		cout << a;
	}
};
int main()
{
	A a;
	a.print<int>(2);
	a.print<string>("小蓝");
}

其实函数模版在类中跟普通函数差别不大,差别比较大的是类中声明类外实现的方式,函数模版不能省略template语句。

class A
{
public:
	template <class _Ty1>
	void print(_Ty1 a);
};
template <class _Ty1>
void A::print(_Ty1 a)
{
	cout << a;
}

普通函数有缺省的写法,同样的,函数模版也有缺省写法。

template <class _Ty1,class _Ty2 = int>
void print(_Ty1 a, _Ty2 b)
{
	cout << a << " " << b;
}
int main()
{
	print<string>("小蓝", 2);
}

函数模版存在传常量的写法:

template <class _Ty1,size_t size>
void printArr(_Ty1 arr)
{
	for (int i = 0;i < size;i ++)
	{
		cout << arr[i] << " ";
	}
}
int main()
{
	int arr[3] = { 1,2,3 };
	printArr<int*, 3>(arr);
}

其中的size_t是无符号整型的别称,这种写法只能显示调用,因为需要传入一个有关大小的常量,但是我们用缺省写法可以做到隐式调用:

template <class _Ty1,size_t size = 3>
void printArr(_Ty1 arr)
{
	for (int i = 0;i < size;i ++)
	{
		cout << arr[i] << " ";
	}
}
int main()
{
	int arr[3] = { 1,2,3 };
	printArr(arr);
}

4.函数模板重载

  • 模板和普通函数 ,调用函数函数类型一致情况 优先调用普通函数
  • 两个模板同时成立,优先调用类型相似度搞的那个
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.声明语法

template <class _Ty>
class A
{

};

2.类模板的声明,实现和调用

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

  • 必须采用显式调用
  • 类模板不是一个实际类型,所以所有用到类名的地方都需要使用: 类名<未知类型> 方式使用
template <class _Ty>
class A
{
public:
	A() {}
	A(string name) :name(name) {}
	void print(_Ty a);    //类中声明在类外实现
protected:
	string name;
};
template <class _Ty>
void A<_Ty>::print(_Ty a) //所有用到类名的地方:都需要用类名<类型>的方式使用   
{
	cout << a << endl;
}

int main()
{
	A<int>a;
	a.print(1);
	A<string>b;
	b.print("小蓝");
}

3.模板类继承

template <class _Ty>
class A
{
public:
	A() {}
	A(string name) :name(name) {}
	void print(_Ty a);    //类中声明在类外实现
protected:
	string name;
};
template <class _Ty>
void A<_Ty>::print(_Ty a) //所有用到类名的地方:都需要用类名<类型>的方式使用   
{
	cout << a << endl;
}
template <class _Ty>
class B :public A<_Ty>
{
public:
	B(string name):A<_Ty>(name){}
};
int main()
{
	B<int> b("小蓝");
	b.print(1);
}

总之,用到模板类的地方都要template修饰一下,继承也是一样。

4.自定义类型当模板参数

注意:模板传入自定义类型,关键在于重载运算符

template <class _Ty>
void print(_Ty a)
{
	cout << a;
}
class A
{
public:
	A(string name,int age):name(name),age(age){}
	friend ostream& operator<<(ostream& out, A& a)
	{
		out << a.name << " " << a.age;
		return out;
	}
protected:
	string name;
	int age;
};
int main()
{
	print(A("小蓝", 18));
}

再来一个练练手:

template <class _Ty>
_Ty Max(_Ty a, _Ty b)
{
	return a > b ? a : b;
}
class A
{
public:
	A(string name,int age):name(name),age(age){}
	friend ostream& operator<<(ostream& out, A& a)
	{
		out << a.name << " " << a.age;
		return out;
	}
	friend bool operator>(A& a,A& b)
	{
		return a.age > b.age;
	}
protected:
	string name;
	int age;
};
int main()
{
	A a1("小蓝", 18);
	A a2("小红", 19);
	A a = Max(a1, a2);
	cout << a;
}

之前设计链表的时候总是感觉底层数据太多难以封装,现在可以试着写一个链表来管理模板类型的数据。

class A
{
public:
	A(string name,int age):name(name),age(age){}
	friend ostream& operator<<(ostream& out,const A& a)
	{
		out << a.name << " " << a.age;
		return out;
	}
protected:
	string name;
	int age;
};
template <class _Ty>
class Node
{
public:
	Node(){}
	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> //因为用到了Node模板
class List
{
public:
	List()
	{
		headNode = nullptr;
	}
	void insertData(_Ty data)
	{
		headNode = new Node<_Ty>(data, headNode);
	}
	void printList()
	{
		Node<_Ty>* pMove = headNode;
		while (pMove)
		{
			cout << pMove->getData() << endl;
			pMove = pMove->getNext();
		}
	}
	~List()
	{
		Node<_Ty>* p = headNode, * q;
		while (p)
		{
			q = p;
			p = p->getNext();
			delete q;
		}
	}
protected:
	Node<_Ty>* headNode;
};
int main()
{
	List<int> list;
	list.insertData(1);
	list.insertData(2);
	list.printList();

	List<A> list2;
	list2.insertData(A("小蓝", 18));
	list2.insertData(A("小宏", 19));
	list2.printList();
}

需要注意的是:提供的接口是返回值,是一个const类型的对象(常属性) 需要把A的重载函数改为const A& a,返回值是值,不可以成为左值 ---> 返回值充当函数参数,需要加const修饰 ,加const是为了传入const 对象的时候这个重载函数也能用。

三.模板嵌套

1.函数模板嵌套

template <class _Ty>
void print(_Ty a)
{
	cout << a;
}
template <class _Ty1,class _Ty2>
class A
{
public:
	A(_Ty1 name,_Ty2 age):age(age),name(name){}
	friend ostream& operator<<(ostream& out, const A& a)
	{
		out << a.name << " " << a.age << endl;
		return out;
	}
protected:
	_Ty1 name;
	_Ty2 age;
};
int main()
{
	print<A<string, int>>(A<string, int>("小蓝",18));
	print(A<string,int>("小红", 19));
	//起别名简化代码
	using AType = A<string,int>;
	print<AType>(AType("小绿", 20));
}

无论外层是隐式调用还是显式调用,里层必须显式调用。

2.类模板嵌套

template <class _Ty1,class _Ty2>
class A
{
public:
	A(_Ty1 name,_Ty2 age):age(age),name(name){}
	friend ostream& operator<<(ostream& out, const A& a)
	{
		out << a.name << " " << a.age ;
		return out;
	}
protected:
	_Ty1 name;
	_Ty2 age;
};
template <class _Ty1,class _Ty2>
class Data
{
public:
	Data(_Ty1 one,_Ty2 two):one(one),two(two){}
	void print()
	{
		cout << one << " " << two << endl;
	}
protected:
	_Ty1 one;
	_Ty2 two;
};
int main()
{
	A<string, int> a("小蓝", 19);
	A<double, double> b(11.1, 12.1);
	Data<A<string, int>, A<double, double>> d(a, b);
	d.print();
	//上面四行等效于
	Data<A<string, int>, A<double, double>> d2(A<string, int>("小蓝", 19), A<double, double>(11.1, 12.1));
	d2.print();
}

其实嵌套就是每一层剥洋葱,没有什么特别的。

四.类模板特化

为啥要特化?因为有些数据是特殊形式的,特殊形式的数据在调用原模板时候可能不兼容,可以把它特殊化到一个单独的类中去实现,针对这种独立的数据类型去做处理

1.局部特化

template <class _Ty1, class _Ty2>
class A
{
public:
	A(_Ty1 one, _Ty2 two) :one(one), two(two) {}
	void print()
	{
		cout << one << " " << two << endl;
	}
protected:
	_Ty1 one;
	_Ty2 two;
};
class Data
{
public:
	Data(int one,int two):one(one),two(two){}
	void print()
	{
		cout << one << " " << two << endl;
	}
protected:
	int one;
	int two;
};
//局部特化,将两种未知类型特化为一种
template <class _Ty>
class A<_Ty,_Ty>
{
public:
	A(_Ty one, _Ty two) :one(one), two(two) {}
	void print()
	{
		one.print();
		two.print();//可以传两个类的对象类型进来 调用各自类对象的方法去打印数据
		cout << "局部特化" << endl;
	}
protected:
	_Ty one;
	_Ty two;
};
int main()
{
	//调用原模板
	A<string, int> a("小蓝", 18);
	a.print();

	//调用局部特化模板
	A<Data, Data> d(Data(1, 2), Data(3, 4));
	d.print();
}

2.完全特化 

 局部特化升级版,传入已知类型。

template <class _Ty1, class _Ty2>
class A
{
public:
	A(_Ty1 one, _Ty2 two) :one(one), two(two) {}
	void print()
	{
		cout << one << " " << two << endl;
	}
protected:
	_Ty1 one;
	_Ty2 two;
};
class Data
{
public:
	Data(int one,int two):one(one),two(two){}
	void print()
	{
		cout << one << " " << two << endl;
	}
protected:
	int one;
	int two;
};
//完全特化,传入指定类型
template <>
class A<Data, Data>
{
public:
	A(Data one, Data two) :one(one), two(two) {}
	void print()
	{
		one.print();
		two.print();//可以传两个类的对象类型进来 调用各自类对象的方法去打印数据
		cout << "完全特化" << endl;
	}
protected:
	Data one;
	Data two;
};
int main()
{
	//调用完全特化模板
	A<Data, Data> d(Data(1, 2), Data(3, 4));
	d.print();
}

 

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值