C++详解 | 模板


一、简介

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。
每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector <int>vector <string>

模板分为函数模板和类模板。

二、函数模板

基本语法:1

template < {typename|class} t1 [ , {typename|class} t2 ... ] > 
ret_type func_name(/*list*/){/*body*/}

其中,template是模板声明关键字,typename(可用class代替)2是形参声明关键字,t1...是形参;第二行就是正常的函数定义。
【实例】:

template<class T>
void outp(T _o){cout<<_o;}
...
outp(12345);
outp("\nhello world!")

输出

12345
hello world!

不难看出,模板的作用类似于对所有数据类型的重载,第2行参数列表中T _o就使用了上一行定义的形参类型T,表示T作为一个占位符名称,可以被任何确切数据类型替换。
请注意,模板形参(<>中)不能为空。
【实例】使用模板函数返回二者最大值

#include<iostream>
using namespace std;

template<class T> T MX(T _a,T _b){return (_a>_b)?_a:_b;}
//模板声明和函数定义也可以写在一行。
int main(){
	cout<<MX(-5,5)<<endl;
	cout<<MX('a','z')<<endl;
	cout<<MX("hello","world")<<endl;
	cout<<MX("world","hello")<<endl;
	cout<<MX("abc","abd")<<endl;
	cout<<MX("abc","xyz")<<endl;
	cout<<MX("abc","cba")<<endl;
	return 0;
}

输出:

5
z
hello
hello
abc
xyz
cba

由此可见,如本例,在使用与函数内操作不符的形参类型时,虽然不会报错,但结果将会非常不可控。

三、模板类

基本语法:

template<{typename|class} t1 [,{typename|class} t2 ...]>
class class_name{/*class body*/};

类似于模板函数,t1...是占位类型名称。
<>中的形参可以使用class关键字声明,但与下一行的类定义关键字class意义不同。

【实例】定义模板类_stack并使其具备STL库中stack<>的性质(使用stack<>书写方便学习)

template<typename t>
class _stack{
private:
	stack<t> _st;//以数据类型t创建栈
public:
	void push(t const&);
	void pop();
	t top() const;//栈顶元素,返回t类型
	bool empty();
	size_t size();
};

//请注意:类外定义函数也要加上模板声明,且_stack<t>的<>必写。
template<typename t>
void _stack<t>::push(t const& _p) {_st.push(_p);}
template<typename t>
void _stack<t>::pop() {_st.pop();}
template<typename t>
t _stack<t>::top() const{return _st.top();}
template<typename t>
bool _stack<t>::empty(){return _st.empty();}
template<typename t>
size_t _stack<t>::size(){return _st.size();}

...

_stack<int> si;
_stack<string> sstr;
_stack<bool> sbl;

三个要点:

  • 即使在类外定义方法,仍需在定义前加上模板声明(就行模板函数一样)
  • 在定义方法时域访问符::前的类名要加<...>写对应的形参。
  • 定义对象时在类名后要加上<...>与模板类的形参对应。如果有多个形参则用,隔开。

四、模板函数重载

模板函数也可以重载,但要求参数个数必须不同(而不仅是参数列表类型不同也行)。

template<class t>
t MX(t a,t b){return (a>b)?a:b;}
template<class t>
t MX(t a,t b,t c){return (a<b)?b:((a>c)?a:c);}

五、函数模板重载

不同的是,这种重载指的是形参列表(<>中)的形参,也要求个数不同(不然怎么区分呢)

template<class t>
void o(t a,t b){cout<<a<<b;}
template<class t,class T>
void o(t a,T b){cout<<a<<b;}

六、关于typename

typename可以用来声明模板形参。
它还有一个作用——使用嵌套依赖类型(nested depended name)

【例】

//须知:类中typedef的类型在类外使用(仅当可访问时)要加【::】
class ex{
	public:
		typedef int T;
		...
	...
};
...
template<class t>
void func(t a){
#if 0
	typedef t::T TT;//非法!error: need 'typename' before 't::T' because 't' is a dependent scope
#endif
	typedef typename t::T TT;//合法。
}

这个时候 typename 的作用就是告诉 c++ 编译器,typename 后面的字符串为一个类型名称,而不是成员函数或者成员变量,这个时候如果前面没有 typename,编译器没有任何办法知道 t::T 是一个类型还是一个成员名称(静态数据成员或者静态函数),所以编译不能够通过。

七、模板分离

如果模板类的声明、定义分别在tmp.h和tmp.cpp中:

|—Proj
	|——tmp.h
	|——tmp.cpp
	|——main.cpp

则在包含时必须同时包含二者(而不是靠tmp.cpp中的包含嵌套):

//main.cpp
#include"tmp.h"
#include"tmp.cpp"
...


@HaohaoCppDebuger |寻兰 
2021/12/2 

-----THE END-----
THANK YOU !

































  1. 语法中{A|B}表示A、B中任选其一。下同。 ↩︎

  2. typenameclass在此处无区别,后者是早期C++的写法。 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值