C++入门篇(15)之模板知识进阶
Posted 捕获一只小肚皮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++入门篇(15)之模板知识进阶相关的知识,希望对你有一定的参考价值。
文章目录
前言
前面的章节,我们大致结束了stl
基础,现在即将向模板进阶和继承多态开始出发
对于模板,博主在前面章节简答的介绍过,而此篇文章主要讲模板的一些更高级应用
模板的非类型参数
在前面的章节中,我们使用到的模板参数一直是class
,但实际上,其模板参数不止有class
,比如我们有此下模板:
#define N 100;
template<class T>
class Array
private:
T array[N];
;
如果我们定义一个对象—Array<int> a;
,可以明显的知道,a的size等于100;
如果我们需要定义一个size等于200的Array对象呢? 很明显,我们需要修改N;
如果我们需要定义一个size等于100,一个size等于200的Array对象
呢? 很明显,我们没办法实现;
而这也就是博主需要引出来的模板的非类型参数
那么我们的非类型参数,应该怎么写呢?
template<class T,int N>
class Array
private:
T array[N];
;
定义size等于100的Array对象: Array<int,100> a;
定义size等于200的Array对象: Array<int,200> a;
注意点:
- 该模板的N其实是常数,一旦传参以后,便不可以修改
- N的类型只能是整型家族成员(int,long,char),不可以为浮点,字符串,自定义类型等等
模板的特化
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,比如
template<class T>
bool IsEqual(const T& left, const T& right)
return left == right;
如果是基础类型,可以得到正确结果,但如果我们的T是char*呢?
比如有char s1[]= "123",char s2[] ="123"
,然后分别传给left和right,那么很明显这样就会得到错误答案;因为此时left和right比较的是指针地址,并不是字符串;
所以,针对这种情况,引出了模板的特化
模板的特化:指对于具体的类型进行特殊化处理; 其中特化又分为全特化和偏特化
①模板的全特化: template后面只有<>,对于需要特化的类型,使用<>写在函数名后或者类名后
template<>
bool Isequal<char*,char*>(char*& left,char* right) //函数模板的特化
return strcmp(left,right); //如果传入char*,就会调用该版本
②模板的偏特化: 即只特化某一部分类型, 其中偏特化又分为两种
第一种: template后面有<>保留但不特化的类型,对于需要特化的类型,使用<>写在函数名后或者类名后
//第一种:只特化一部分的类型
template<class T2>
class Isequal<char*,T2> //只特化第一个类型
public:
Date()cout<<"全特化:char* ,T2"
第二种: 告诉我们特化的是什么类型
//第二种,告诉我们特化的是什么类型
template<class T1,class T2>
class Isequal<T1*,T2*> //告诉我们如果传的是指针,那么就会调用此模板
public:
Date()cout<<"全特化:T* ,T*"
template<class T1,class T2>
class Isequal<T1&,T2&> //告诉我们如果传的是引用,那么就会调用此模板
public:
Date()cout<<"全特化:T& ,T&"
模板的分离编译问题
对于函数来说,它支持分离编译(即函数的声明写在.h
文件中,函数的定义写在.cpp
文件中);
但是对于模板来说,并不支持这样,原因是为何呢?我们先看一下程序在运行之前(编译),会经历一些什么阶段
假设目前有三个文件,分别是hp.h
,hp.cpp
,test.cpp
,其中前两个分别是对应的分离文件
hp.h
的内容有:
#pragma once
int func();
template <class T> class Array;
hp.cpp
的内容有:
#pragma once
int func()
return 5;
template <class T>
bool is(int i)
return i%2 ? true :false;
test.cpp
的内容为:
#include <iostream>
#include <hp.h>
using namespace std;
int main()
func();
is(2);
return 0;
在前面的c语言章节,博主讲解过程序的预处理,我们知道,上面三个文件将会经理下面四个阶段:
- 预编译: 将
hp.cpp
和test.cpp
分别变成以后缀.i
结尾的文件,在这个阶段,会发生 删除注释,展开头文件,宏替换和条件编译 - 编译: 将
hp.i
和test.i
分别变成以后缀.s
结尾的文件,在这个阶段,会发生 检查语法错误,生成汇编语言 - 汇编: 将
hp.s
和test.s
分别变成以后缀.o
结尾的文件,在这个阶段,会发生 将汇编生成二进制 - 链接: 寻找地址
前面三个阶段,我们可能很好理解,但是链接这里有点迷糊,举个例子:
test.cpp
文件在变成二进制之前,按照上面步骤,它会把hp.h
的内容展开到自己文件,但是这仅仅只是个声明,主函数调用两个函数时候,并不能找到函数的定义,这时候系统会给这两个函数打一个标记,代表先不管它; 等前三个阶段完成以后,链接阶段干的就是去找函数定义的位置,然后把地址发送给主函数里面的两个调用函数;
而正是这一个步骤,刚好模板会报错,为什么呢? 我们在刚学模板的时候直到,模板只有调用才会生成对于的代码;
在hp.cpp
中,只有模板的定义,没有调用,那么在第一个阶段,预编译
时候模板定义的代码就被删除了
等到链接阶段,test.cpp
去找寻模板地址时候,发现找不到定义模板的地址,就自然会报错了
因此,模板并不支持分离编译,记好哦~~
以上是关于C++入门篇(15)之模板知识进阶的主要内容,如果未能解决你的问题,请参考以下文章