windows7怎么做网站服务器,东莞做网站软件,官方app下载,招聘网站开发的要求⭐博客主页#xff1a;️CS semi主页 ⭐欢迎关注#xff1a;点赞收藏留言 ⭐系列专栏#xff1a;C初阶 ⭐代码仓库#xff1a;C初阶 家人们更新不易#xff0c;你们的点赞和关注对我而言十分重要#xff0c;友友们麻烦多多点赞#xff0b;关注#xff0c;你们的支持是我… ⭐博客主页️CS semi主页 ⭐欢迎关注点赞收藏留言 ⭐系列专栏C初阶 ⭐代码仓库C初阶 家人们更新不易你们的点赞和关注对我而言十分重要友友们麻烦多多点赞关注你们的支持是我创作最大的动力欢迎友友们私信提问家人们不要忘记点赞收藏关注哦 模板 前言一、模板参数分类1、介绍2、定长数组的非类型模板参数2、利用类型形参进行不同容器适配打印3、介绍typename 二、模板的特化1、概念2、函数模板特化3、类模板特化1全特化2偏特化i、部分特化ii、参数更进一步的限制 4、总结 三、模板分离编译stack和普通函数的例子1func1通过和fun2崩溃的原因2size()通过的原因3push()和pop()崩溃的原因三种解决方法1、显示实例化2、同一文件3、.hpp方式 四、模板总结 前言
模板进阶体现了很多的不同的方法和思想这里提供了不同的分类和特化以及分离编译我们需要掌握百分之八九十并在后面慢慢打磨不断历练。 一、模板参数分类
1、介绍 模板参数分类类型形参与非类型形参。 类型形参出现在模板参数列表中跟在class或者typename之类的参数类型名称。 非类型形参是用一个常量作为类(函数)模板的一个参数在类(函数)模板中可将该参数当成常量来使用。 // 非模板常量的优势传多少就是多少很方便
// 非模板常量的局限1、必须是整型 2、是一个常量
templateclass T, size_t N // N是一个常量 不能被修改
class Stack
{
public:
private:T _a[N];int _top;
};int main()
{Stackint, 10 st1; // 10个空间Stackint, 100 st2; // 100个空间return 0;
}浮点数、类对象以及字符串是不允许作为非类型模板参数的。非类型的模板参数必须在编译期就能确认结果。
2、定长数组的非类型模板参数
我们来简单了解一个定长数组就是用这个非类型模板参数来解决的。
这是简单的数组
这是定长数组 虽然看起来没什么问题但是最奇怪的一个点为为什么定长数组不初始化所有的呢这个就是委员会没有进行更新的原因了。那么定长数组唯一的优势是检查越界很有用我们看下面的代码
2、利用类型形参进行不同容器适配打印
#includeiostream
#includevector
#includelist
using namespace std;templateclass Container
void Print(const Container v)
{// 编译不确定Container::const_iterator是类型还是对象// typename就是明确告诉编译器这是类型等模板实例化再去找typename Container::const_iterator it v.begin();// auto是类型所以可以用auto//auto it v.begin();while (it ! v.end()){cout *it ;it;}cout endl;
}int main()
{vectorint v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout e ;}cout endl;Print(v);listint lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);Print(lt);return 0;
}设计一个容器我们在main函数中设计简单的容器并将其传到Print函数中Print函数中利用模板自行去寻找相对应的容器进行输出打印。
3、介绍typename
以前我们讲typename和class一样但是这里我们讲一个区别的点当我们分不清一个代码到底是类型还是对象还是变量的时候我们加一个typename告诉编译器这个是类型不是对象因为编译器没那么聪明加入进来一个静态的变量呢编译器根本分不清如果编译器茫然地去寻找那肯定是不符合规定的。而编译器知道了这是个类型会在类模板实例化的时候自动去找这个容器类型的实现规则并进行操作。 二、模板的特化
1、概念
通常情况下使用模板可以实现一些与类型无关的代码但对于一些特殊类型的可能会得到一些错误的结果需要特殊处理比如实现了一个专门用来进行小于比较的函数模板 我们发现我们传参数过去比较的是地址大小没有实现我们的目的所以我们有了一个模板的特化帮助我们进行实现传参的比较。
2、函数模板特化
函数模板的特化步骤
必须要先有一个基础的函数模板关键字template后面接一对空的尖括号函数名后跟一对尖括号尖括号中指定需要特化的类型函数形参表: 必须要和模板函数的基础参数类型完全相同如果不同编译器可能会报一些奇怪的错误。 但一般情况下如果函数模板遇到不能处理或者处理有误的类型为了实现简单通常都是将该函数直接给出。 该种实现简单明了代码的可读性高容易书写因为对于一些参数类型复杂的函数模板特化时特别给出因此函数模板不建议特化。
3、类模板特化
1全特化
全特化即是将模板参数列表中所有的参数都确定化。 2偏特化
偏特化任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类
i、部分特化
将模板参数类表中的一部分参数特化。 ii、参数更进一步的限制
偏特化并不仅仅是指特化部分参数而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。 4、总结
templateclass T1, class T2
class Data
{
public:Data() { cout Data(T1, T2) endl; }
};// 全特化
template
class Dataint, double
{
public:Data() { cout Data(int, double) endl; }
};// 偏特化 -- 特化部分参数
// 可能对某些类型的进一步限制
templateclass T1
class DataT1, double
{
public:Data() { cout Data(T1, double) endl; }
};// 偏特化 -- 特化成指针
templateclass T1, class T2
class DataT1*, T2*
{
public:Data() { cout Data(T1*, T2*) endl; }
};//两个参数偏特化为引用类型
template typename T1, typename T2
class Data T1, T2
{
public:Data(const T1 d1, const T2 d2): _d1(d1), _d2(d2){cout DataT1, T2 endl;}private:const T1 _d1;const T2 _d2;
};int main()
{Dataint, int d1;Dataint, double d2;Dataint*, double d3;Datadouble, double d4;return 0;
}三、模板分离编译
stack和普通函数的例子
举个简单的例子 mobel.h:
#includeiostream
#includevector
#includedequenamespace JRH
{// 容器适配器templateclass T, class Container std::dequeTclass stack{public:// 插入 尾插void push(const T x);// 删除 尾删void pop();// 取栈顶元素T top(){return _con.back();}// 输出个数size_t size(){return _con.size();}// 判空bool empty(){return _con.empty();}private:Container _con;};class A{public:void func1();void func2();};
}mobel.cpp:
#includemobel.hnamespace JRH
{templateclass T, class Container// 插入 尾插void stackT, Container::push(const T x){_con.push_back(x);}templateclass T, class Containervoid stackT, Container::pop(){_con.pop_back();}void A::func1(){}}test.cpp:
#includeiostream
#includevector
#includelist
#includearray
using namespace std;#includemobel.hint main()
{JRH::stackint st;st.push(1); // 崩溃st.push(2); // 崩溃st.pop(); // 崩溃st.size(); // 通过JRH::A a;a.func1(); // 通过a.func2(); // 崩溃return 0;
}我们分析一下main函数中有些函数是通过的有些函数是崩溃的原因
1func1通过和fun2崩溃的原因
func1是在汇编的时候将定义的func1函数转化成地址指令然后在链接的时候直接找到func1定义的地址并进行链接链接成功能够通过运行。
func2没有定义自然在链接的时候找不到定义的地址链接不成功直接崩溃。
2size()通过的原因
size()本来就能够通过因为它的定义和声明不是分离的是合在一起的在编译的时候直接能找到了。
3push()和pop()崩溃的原因
push()和pop()因为有个模板参数也就是在汇编的时候转化成为xxpushi(?)与xxpushxx(?)不同而我们的定义是一个模板没有进行实例化它没有指定类型是int还是double还是char它并不知晓是什么同样也没有地址。所以链接失败。
三种解决方法
1、显示实例化
一种巧妙的方法使用模板显示实例化不推荐。
mobel.cpp:
2、同一文件
还有一种最好的方法stl也是这样做的就是将声明定义分离在同一文件中
3、.hpp方式
最后一种好的方法是 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。 四、模板总结
【优点】
模板复用了代码节省资源更快的迭代开发C的标准模板库(STL)因此而产生。增强了代码的灵活性 例如适配器和仿函数。
【缺陷】
模板会导致代码膨胀问题也会导致编译时间变长。出现模板编译错误时错误信息非常凌乱不易定位错误。