目录
一、模板参数:
template<class T>
模板怎么理解?
模板就是参数
模板是一个半成品,少一个东西实例化
模板就像是一个模具,丢进去什么东西,就变成什么东西
你丢进去铁,就变成铁剑
你丢进去金,就变成金剑
你丢进去银,就变成银剑
你丢进去铜,就变成铜剑
剑的作用都是一样的,但是可能不同的剑用于不同的场景
函数参数:(T 对象)
模板参数:<class 类型>
模板分:函数模板(可以推导),类模板(必须实例化)
二、非类型模板(传常量)
目前只支持整型int char unsigned_int size_t
1、定义:
template <size_t N>
2、理解
一般传常量使用宏
但是利用宏有一个致命缺点:就是不可改变
例如,我要一个10个大小的数组,如果宏是10还好说
如果我要的是20呢?1000000000呢?
宏无法解决,要解决,只有再写一个类
而有了非类型模板参数,就可以了。
数组参数为N
你传N的参数为10,就开辟10个空间
你传N的参数为100000,就开辟100000个空间
那么,它是这么解决呢?
事实上,本质上还是生成了多个类
你传10,编译器生成10的类
你传1000,编译器生成1000的类
...
以此类推
所以,事实上是原本要你自己写的类
变成了编译器帮你写
3、应用场景
我们想要控制一个静态数组的大小
注意,不是动态开,而是静态。动态开辟有new
静态开辟是在编译阶段就直接开好了
动态开辟是在运行阶段开辟
二、模板的缺省参数
直接给缺省
template <size_t N = 10>
三、按需实例化
1、理解
在编译过程中,编译器会根据需求实例化,生成具体的类,再进行语法编译
什么意思?
我有一个模板类,类内部有100个成员函数,但是我只用到了其中一个
例如有100个成员函数,但是我只用了一个,剩下的99个和程序运行毫无关系,我有必要加载吗?
没有必要
所以,我生成的类,并不是按照模板严格的从头到尾全部加载
而是,我需要什么,我就加载什么
实例化时,会按需实例化
什么意思?
调用那个成员函数,就实例化那个成员函数
例如说,要调用成员函数a,就只实例化了a
但是对于不被调用的成员函数b,就不会实例化
什么叫做实例化?说白了就是用没用,实例化了,就是用到了;没有实例化,就是没有用到
假如你的成员函数b内部有语法错误
但是因为没有实例化,也就是没有用到
既然都没有用到,我编译器有必要对你进行检查吗?
完全没有必要
所以,不检查,那你有错误,我能知道吗?
不知道
所以,即使你有语法问题,不会报错,但是我没有检查,会报错吗?
不会报错。
2、具体实例
namespace me
{
template<class T>
class A
{
public:
void func1( )
{
cout << "no problem" << endl;
}
void func2()
{
cout << "problem" << endl;
func1(1,2,3,4,5,6,7,8,9,10);
}
private:
T* _a;
};
}
void test_template_instantiation()
{
me::A<int> a;
a.func1();
}
a、上述代码明显是错误的
b、只调用func1运行没有问题
c、调用func2就会出问题
四、模板特化
1、函数模板特化
a、定义
模板传具体的类型A
template<>
bool func(A a1, A a2)
{...}
b、理解
模板的行为不符合我们的预期
例如,模板的比较都是对具体的整型x和y进行比较,然后返回结果
但是,现在我需要用地址比较
也就是要比较*x和*y
但是原模板比较的依旧是x和y,如果不解引用就变成了比较地址
这个时候,原来模板不能处理
所以,需要针对特殊类型进行特殊处理
只能写一个
那么我们写的这个,不是模板
而是区别于模板的特殊类,所以叫做特化
本质是对函数模板的重载
2、类模板特化
模板是匹配,不是替换
a、定义格式
1)类模板
//类模板
template<class T1,class T2>
class A
{
public:
A()
{
cout <<"A<T1,T2>" << endl;
}
private:
T1 _a;
T2 _b;
};
2)半特化/偏特化
//半特化/偏特化
template<class T>
class A<T,char>
{
public:
A()
{
cout << "A<T,class>" << endl;
}
};
3)全特化
//全特化
template<>
class A<int ,int >
{
public:
A()
{
cout << "A<int,int>" << endl;
}
};
4)按需调用
b、理解
和函数模板的理解是一样的
依旧是类模板的行为不符合我们的预期
五、模板声明定义分离
1、不支持声明定义分离
a、为什么?
如果定义分离,那么声明在头文件,定义在实现文件
一个程序的运行需要经过:预处理、编译、链接三个阶段
模板是在编译阶段的时候实例化
在编译阶段是没有链接的,即各个文件是独立的,没有联系的
那么,在编译模板的时候
头文件有声明,但是没有定义
实现文件有定义,但是它不知道要怎么实例化!
就是我不知道你模板要实例化成什么
而模板的原则是按需实例化
所以,这就导致了模板没有进行实例化
那么到了链接的时候,去找模板定义的时候,找不到
b、解决办法?
有解决办法吗?没有,这是设计机制的问题
但是其实还可以显式实例化
就是在定义文件再写一个声明,告诉模板要实例化成什么
但是,这纯粹是蛋疼家才会选择的方式
所以,闲着没事就不要声明定义分离了
六、模板的优缺点
1、优点
a、代码更加灵活
b、模板复用了代码,节省了很多自己写代码的时间
2、缺点
a、代码膨胀,编译时间变长
b、如果出现错误,不好定位,因为不知道到底是那个实例化出的问题,需要逐个排除