【C++】11.模板进阶

文章介绍了C++中的模板概念,包括非类型参数的使用,如在Array类中用size_t作为模板参数。接着讲述了模板的特化,用于处理特定类型时的特殊情况,如函数模板IsEqual对char*的特化。此外,讨论了模板的全特化和偏特化,并举例说明。文章还提到了模板分离编译的问题及其解决方案,指出模板需在同一个头文件中定义和实例化,以避免链接错误。最后总结了模板的优缺点,如代码复用和编译时的代码膨胀问题。
摘要由CSDN通过智能技术生成

1.非类型模板参数

模板参数分类类型形参与非类型形参。

  • 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
  • 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
//非类型参数+非类型模板参数
template<class T, size_t N = 10>
//float double string不能做非类型模板参数
//int short char long  long long可以 整形
class Array
{
public:
    //Array()
    //{
    //	N = 10;//无法修改 N是常量 修改会报错
    //}
private:
    T _a[N];
};
int main()
{
    Array<int,100> a1; //100
    Array<int,1000> a2; //1000
    return 0;
}

2.模板的特化

1°概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得

到一些错误的结果。此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊

类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。

//模板的特化
template<class T>
bool IsEqual(T& left, T& right)
{
    return left == right;
}

//特化 (针对某些类型的特殊化处理) 加在函数名后面
template<>
bool IsEqual<char*>(char*& left, char*& right)
{
    return strcmp(left, right) == 0;
}
  • 有可能是字符串的比较 此时需要进行特殊的处理
  • template<>+函数名后<类型名>

2°分类

1*全特化

//接下来对这个类进行特化
template<class T1,class T2>
class Data
{
public:
    Data() { cout << "Data<T1,T2>" << endl; }
private:
    T1 _d1;
    T2 _d2;
};

//特化 加在类名后面
//全特化 全部的参数都特化
template<>
class Data<int,char>
{
public:
    Data() { cout << "全特化:Data<int,char>" << endl; }
private:
};

2*偏特化

//偏特化/半特化
//第一种
template<class T2>
class Data<int, T2>
{
public:
    Data() { cout << "偏特化:Data<int,T2>" << endl; }
private:
};
//第二种
template<class T1,class T2>
class Data<T1*, T2*>
{
public:
    Data() { cout << "偏特化:Data<T1*,T2*>" << endl; }
private:
};
//第三种
template<class T1, class T2>
class Data<T1&, T2&>
{
public:
    Data() { cout << "偏特化:Data<T1&,T2&>" << endl; }
private:
};

3*测试

int main()
{
    int a = 0, b = 1;
    cout << IsEqual(a, b) << endl;
    const char* p1 = "hello";
    const char* p2 = "world";
    cout << IsEqual(p1, p2) << endl;

    Data<int, int> d1;//偏特化
    Data<int, char> d2;//调全特化版本
    Data<int,double> d3;//调偏特化版本
    Data<char,char> d4;//普通版本
    //第一个是int就调半特化 如果第二个是char那就是全特化
    //第一个不是int 就调普通版本
    Data<char*, char*> d5;
    Data<char*, int*> d6;
    Data<char&, int&> d7;
    //可调指针 引用
    return 0;
}

3.模板分离编译

  • 预处理过程分为几步?具体是干什么的?

Func.h Func.cpp Test.cpp

1.预处理 展开头文件/宏替换/条件编译/去掉注释

Func.i Test.i

2.编译 检查语法 生成汇编代码

Func.s Test.s

3.汇编 将汇编代码转成二进制的机器码

Func.o Test.o

4.链接 将目标文件合到一起 编译时F1和F2函数由声明 所以编译过来 链接时要去Func.o中找F1和F2的地址

a.out 这里F1找到了 F2没找到 所以报了一个链接错误

分离编译

项目工程中一般将函数或者类的声明放到.h

将函数或者类的定义放到.cpp

为什么要分离编译呢? 方便查看和维护

  • 同样式分离编译 普通函数/类可以 函数模板/类模板为什么不行?

最后链接错误 普通函数和类可以找到地址 而模板函数/类模板找不到

  • 解决方法:

    1.将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。推荐使用这种。

    2.模板定义的位置显式实例化。

最好使用1 都放到.h中就可以了

#include "Func.h"

int main()
{
    F1();        //可以调到 call 一个函数地址
    F2(10);      //模板的话 出了问题 链接错误
    //函数模板找不到相关地址 符号表里面没有F2()
    //显示实例化后可以找到
    F2(10.11);//突然来个double 又找不到了 用一个就要实例化一个
    F2('a');
    return 0;
}
#pragma once
#include <iostream>
using namespace std;

void F1();

template<class T>
void F2(const T& x)
{
    cout << "void F2(const T& x)" << endl;
}
#include "Func.h"

void F1()
{
    cout << "F1()" << endl;
}

//template<class T>
//void F2(const T& x)
//{
//	cout << "void F2(const T& x)" << endl;
//}

解决模板不能分离
1.显示实例化 不常用 因为不方便
2.粗暴 就是不要分离编译了
//template
//void F2<int>(const int& x);
//
//template
//void F2<double>(const double& x);
//直接放到.h里
//所以模板放.h
//函数都可以

4.总结

优点:

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  2. 增强了代码的灵活性

缺点:

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

努力的小恒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值