目录

一.函数模板

1.定义和声明

2.调用函数模版

3.函数模版在类中的应用

4.函数模板重载

二.类模板

1.声明语法

2.类模板的声明,实现和调用

3.模板类继承

4.自定义类型当模板参数

三.模板嵌套

1.函数模板嵌套

2.类模板嵌套

四.类模板特化

1.局部特化

2.完全特化


一.函数模板

什么是模板?简单来说,就是一段任何类型都通用的代码,比如我们定义一个比较大小的函数,那这个函数可以适合整型数据,也可以适合浮点型,甚至可以适合string类型,这种把类型当作未知量,可以忽略类型影响的类或函数,我们称之为模版。先来了解函数模版。

1.定义和声明

如何声明和定义一个函数模版?下面以一个比较大小的函数为例:

template <typename _Ty>
_Ty Max(_Ty a, _Ty b)
{return a > b ? a : b;
}

也可以同时定义多个未知类型:

template <typename _Ty1, typename _Ty2>
void print(_Ty1 a, _Ty2 b)
{cout << a << " " << b;
}

如果觉得typename太陌生的话,也可以将它换成class,这也是可以的。

template <class C>
void printC(C c)
{cout << c;
}

2.调用函数模版

调用函数模版有隐式调用和显式调用两种,先来看隐式调用:

template <typename _Ty>
_Ty Max(_Ty a, _Ty b)
{return a > b ? a : b;
}
int main()
{cout << Max(2, 6) << " " << Max(2.1, 2.2);
}

可以看到,隐式调用跟正常的函数传参没什么区别,它会自己识别你的类型,再来看看显式调用:

template <typename _Ty>
_Ty Max(_Ty a, _Ty b)
{return a > b ? a : b;
}
int main()
{cout << Max<int>(2, 6) << " " << Max<double>(2.1, 2.2);
}

可以看到,显示调用的格式是函数名<类型名>(参数)。同样,多个未知类型的调用也是如此:

template <typename _Ty1, typename _Ty2>
void print(_Ty1 a, _Ty2 b)
{cout << a<< " " << b;
}
int main()
{print(1, 1.2);print<int, double>(1, 2.1);
}

3.函数模版在类中的应用

class A
{
public:template <class _Ty1>void print(_Ty1 a){cout << a;}
};
int main()
{A a;a.print<int>(2);a.print<string>("小蓝");
}

其实函数模版在类中跟普通函数差别不大,差别比较大的是类中声明类外实现的方式,函数模版不能省略template语句。

class A
{
public:template <class _Ty1>void print(_Ty1 a);
};
template <class _Ty1>
void A::print(_Ty1 a)
{cout << a;
}

普通函数有缺省的写法,同样的,函数模版也有缺省写法。

template <class _Ty1,class _Ty2 = int>
void print(_Ty1 a, _Ty2 b)
{cout << a << " " << b;
}
int main()
{print<string>("小蓝", 2);
}

函数模版存在传常量的写法:

template <class _Ty1,size_t size>
void printArr(_Ty1 arr)
{for (int i = 0;i < size;i ++){cout << arr[i] << " ";}
}
int main()
{int arr[3] = { 1,2,3 };printArr<int*, 3>(arr);
}

其中的size_t是无符号整型的别称,这种写法只能显示调用,因为需要传入一个有关大小的常量,但是我们用缺省写法可以做到隐式调用:

template <class _Ty1,size_t size = 3>
void printArr(_Ty1 arr)
{for (int i = 0;i < size;i ++){cout << arr[i] << " ";}
}
int main()
{int arr[3] = { 1,2,3 };printArr(arr);
}

4.函数模板重载

  • 模板和普通函数 ,调用函数函数类型一致情况 优先调用普通函数
  • 两个模板同时成立,优先调用类型相似度搞的那个
void print(int a, string b)
{cout << "普通函数" << endl;
}
template <class _Ty1,class _Ty2>
void print(_Ty1 a, _Ty2 b)
{cout << "两个类型" << endl;
}template <class _Ty>
void print(_Ty a, _Ty b)
{cout << "一个类型" << endl;
}int main()
{print<int, string>(12, "调用模板");print(12, string("优先调用适应的普通函数"));//两个模板同时成立,优先调用类型相似度搞的那个print(12, 12); // return 0;
}

二.类模板

1.声明语法

template <class _Ty>
class A
{};

2.类模板的声明,实现和调用

  • 多文件中,类模板 中的声明和实现一定在一起的,不能分开写

  • 必须采用显式调用
  • 类模板不是一个实际类型,所以所有用到类名的地方都需要使用: 类名<未知类型> 方式使用
template <class _Ty>
class A
{
public:A() {}A(string name) :name(name) {}void print(_Ty a);    //类中声明在类外实现
protected:string name;
};
template <class _Ty>
void A<_Ty>::print(_Ty a) //所有用到类名的地方:都需要用类名<类型>的方式使用
{cout << a << endl;
}int main()
{A<int>a;a.print(1);A<string>b;b.print("小蓝");
}

3.模板类继承

template <class _Ty>
class A
{
public:A() {}A(string name) :name(name) {}void print(_Ty a);    //类中声明在类外实现
protected:string name;
};
template <class _Ty>
void A<_Ty>::print(_Ty a) //所有用到类名的地方:都需要用类名<类型>的方式使用
{cout << a << endl;
}
template <class _Ty>
class B :public A<_Ty>
{
public:B(string name):A<_Ty>(name){}
};
int main()
{B<int> b("小蓝");b.print(1);
}

总之,用到模板类的地方都要template修饰一下,继承也是一样。

4.自定义类型当模板参数

注意:模板传入自定义类型,关键在于重载运算符

template <class _Ty>
void print(_Ty a)
{cout << a;
}
class A
{
public:A(string name,int age):name(name),age(age){}friend ostream& operator<<(ostream& out, A& a){out << a.name << " " << a.age;return out;}
protected:string name;int age;
};
int main()
{print(A("小蓝", 18));
}

再来一个练练手:

template <class _Ty>
_Ty Max(_Ty a, _Ty b)
{return a > b ? a : b;
}
class A
{
public:A(string name,int age):name(name),age(age){}friend ostream& operator<<(ostream& out, A& a){out << a.name << " " << a.age;return out;}friend bool operator>(A& a,A& b){return a.age > b.age;}
protected:string name;int age;
};
int main()
{A a1("小蓝", 18);A a2("小红", 19);A a = Max(a1, a2);cout << a;
}

之前设计链表的时候总是感觉底层数据太多难以封装,现在可以试着写一个链表来管理模板类型的数据。

class A
{
public:A(string name,int age):name(name),age(age){}friend ostream& operator<<(ostream& out,const A& a){out << a.name << " " << a.age;return out;}
protected:string name;int age;
};
template <class _Ty>
class Node
{
public:Node(){}Node(_Ty data,Node<_Ty>* next):data(data),next(next){}_Ty getData(){return data;}Node<_Ty>* getNext(){return next;}
protected:_Ty data;Node<_Ty>* next;
};
template <class _Ty> //因为用到了Node模板
class List
{
public:List(){headNode = nullptr;}void insertData(_Ty data){headNode = new Node<_Ty>(data, headNode);}void printList(){Node<_Ty>* pMove = headNode;while (pMove){cout << pMove->getData() << endl;pMove = pMove->getNext();}}~List(){Node<_Ty>* p = headNode, * q;while (p){q = p;p = p->getNext();delete q;}}
protected:Node<_Ty>* headNode;
};
int main()
{List<int> list;list.insertData(1);list.insertData(2);list.printList();List<A> list2;list2.insertData(A("小蓝", 18));list2.insertData(A("小宏", 19));list2.printList();
}

需要注意的是:提供的接口是返回值,是一个const类型的对象(常属性) 需要把A的重载函数改为const A& a,返回值是值,不可以成为左值 ---> 返回值充当函数参数,需要加const修饰 ,加const是为了传入const 对象的时候这个重载函数也能用。

三.模板嵌套

1.函数模板嵌套

template <class _Ty>
void print(_Ty a)
{cout << a;
}
template <class _Ty1,class _Ty2>
class A
{
public:A(_Ty1 name,_Ty2 age):age(age),name(name){}friend ostream& operator<<(ostream& out, const A& a){out << a.name << " " << a.age << endl;return out;}
protected:_Ty1 name;_Ty2 age;
};
int main()
{print<A<string, int>>(A<string, int>("小蓝",18));print(A<string,int>("小红", 19));//起别名简化代码using AType = A<string,int>;print<AType>(AType("小绿", 20));
}

无论外层是隐式调用还是显式调用,里层必须显式调用。

2.类模板嵌套

template <class _Ty1,class _Ty2>
class A
{
public:A(_Ty1 name,_Ty2 age):age(age),name(name){}friend ostream& operator<<(ostream& out, const A& a){out << a.name << " " << a.age ;return out;}
protected:_Ty1 name;_Ty2 age;
};
template <class _Ty1,class _Ty2>
class Data
{
public:Data(_Ty1 one,_Ty2 two):one(one),two(two){}void print(){cout << one << " " << two << endl;}
protected:_Ty1 one;_Ty2 two;
};
int main()
{A<string, int> a("小蓝", 19);A<double, double> b(11.1, 12.1);Data<A<string, int>, A<double, double>> d(a, b);d.print();//上面四行等效于Data<A<string, int>, A<double, double>> d2(A<string, int>("小蓝", 19), A<double, double>(11.1, 12.1));d2.print();
}

其实嵌套就是每一层剥洋葱,没有什么特别的。

四.类模板特化

为啥要特化?因为有些数据是特殊形式的,特殊形式的数据在调用原模板时候可能不兼容,可以把它特殊化到一个单独的类中去实现,针对这种独立的数据类型去做处理

1.局部特化

template <class _Ty1, class _Ty2>
class A
{
public:A(_Ty1 one, _Ty2 two) :one(one), two(two) {}void print(){cout << one << " " << two << endl;}
protected:_Ty1 one;_Ty2 two;
};
class Data
{
public:Data(int one,int two):one(one),two(two){}void print(){cout << one << " " << two << endl;}
protected:int one;int two;
};
//局部特化,将两种未知类型特化为一种
template <class _Ty>
class A<_Ty,_Ty>
{
public:A(_Ty one, _Ty two) :one(one), two(two) {}void print(){one.print();two.print();//可以传两个类的对象类型进来 调用各自类对象的方法去打印数据cout << "局部特化" << endl;}
protected:_Ty one;_Ty two;
};
int main()
{//调用原模板A<string, int> a("小蓝", 18);a.print();//调用局部特化模板A<Data, Data> d(Data(1, 2), Data(3, 4));d.print();
}

2.完全特化

局部特化升级版,传入已知类型。

template <class _Ty1, class _Ty2>
class A
{
public:A(_Ty1 one, _Ty2 two) :one(one), two(two) {}void print(){cout << one << " " << two << endl;}
protected:_Ty1 one;_Ty2 two;
};
class Data
{
public:Data(int one,int two):one(one),two(two){}void print(){cout << one << " " << two << endl;}
protected:int one;int two;
};
//完全特化,传入指定类型
template <>
class A<Data, Data>
{
public:A(Data one, Data two) :one(one), two(two) {}void print(){one.print();two.print();//可以传两个类的对象类型进来 调用各自类对象的方法去打印数据cout << "完全特化" << endl;}
protected:Data one;Data two;
};
int main()
{//调用完全特化模板A<Data, Data> d(Data(1, 2), Data(3, 4));d.print();
}

C++学习笔记(11)相关推荐

  1. SpringMVC:学习笔记(11)——依赖注入与@Autowired

    SpringMVC:学习笔记(11)--依赖注入与@Autowired 使用@Autowired 从Spring2.5开始,它引入了一种全新的依赖注入方式,即通过@Autowired注解.这个注解允许 ...

  2. Hadoop学习笔记—11.MapReduce中的排序和分组

    Hadoop学习笔记-11.MapReduce中的排序和分组 一.写在之前的 1.1 回顾Map阶段四大步骤 首先,我们回顾一下在MapReduce中,排序和分组在哪里被执行: 从上图中可以清楚地看出 ...

  3. HALCON 20.11:深度学习笔记(11)---目标检测

    HALCON 20.11:深度学习笔记(11)---目标检测 HALCON 20.11.0.0中,实现了深度学习方法. 本章讲解了如何使用基于深度学习的对象检测. 通过对象检测,我们希望在图像中找到不 ...

  4. 台大李宏毅Machine Learning 2017Fall学习笔记 (11)Convolutional Neural Network

    台大李宏毅Machine Learning 2017Fall学习笔记 (11)Convolutional Neural Network 本博客主要整理自: http://blog.csdn.net/x ...

  5. 华为HCIA-datacom 学习笔记11——AAA原理与配置

    华为HCIA-datacom 学习笔记11--AAA原理与配置 AAA原理与配置 1.AAA概述 认证(authentication):验证用户是否获得访问权,确定哪些用户可以访问网络 授权(auth ...

  6. 点云学习笔记11——VoxelNet算法+代码运行

    点云学习笔记11--VoxelNet算法+代码运行 一.算法分析 摘要 介绍 相关工作 1.2. 贡献 2.VoxelNet 2.1.特征学习网络 2.1.1 特征学习网络 二.代码复现 2.1.环境 ...

  7. 凸优化学习笔记 11:对偶原理 拉格朗日函数

    前面讲了凸优化问题的定义,以及一些常见的凸优化问题类型,这一章就要引入著名的拉格朗日函数和对偶问题了.通过对偶问题,我们可以将一些非凸问题转化为凸优化问题,还可以求出原问题的非平凡下界,这对复杂优化问 ...

  8. Python学习笔记11:函数修饰符

    Python学习笔记11:函数修饰符 Python有很多有趣的特性,其中函数修饰符就是一个. 我们在之前的那个web应用示例中用过如下写法: @web.route('/log') @符号后边的,就是一 ...

  9. MIPS汇编语言学习笔记11:整数减法 (mult方法)

    任务:整数相乘,并输出打印. 代码: .dataintA: .word 5intB: .word 2 .textli $v0, 1lw $a1, intAlw $a2, intBmult $a1, $ ...

  10. 从零写一个具有IOC-AOP-MVC功能的框架---学习笔记---11. MVC功能之http请求处理器的编写---简易框架最后一公里!

    从零写一个具有IOC-AOP-MVC功能的框架-学习笔记 专栏往期文章链接: IOC功能相关章节: 从零写一个具有IOC-AOP-MVC功能的框架-学习笔记-01.项目初始化 从零写一个具有IOC-A ...

最新文章

  1. Flex中如何通过horizontalTickAligned和verticalTickAligned样式指定线图LineChart横竖方向轴心标记的例子...
  2. 大家说火箭进决赛的机会有多大?
  3. ajax中加上AntiForgeryToken防止CSRF攻击
  4. 那些年收藏的技术文章(一) CSDN篇
  5. 天池 在线编程 推荐朋友(哈希)
  6. Java中List转换为数组,数组转List
  7. Python 数据可视化利器 plus(plotly )
  8. android jni 机器码,NDK构建
  9. 集成学习 Bagging, Boosting, Stacking
  10. 996是人类社会的倒退
  11. linux 安装yum 安装php
  12. 完全卸载Office 2016 for Mac教程
  13. 第十七章_模型压缩、加速及移动端部署
  14. word文档css格式化,CSS的Word中的列表详解
  15. excel合并多个表格的快速操作
  16. 自动计数报警器c语言,基于51单片机的光电式计数报警器
  17. 开源中国 Android 文件夹管理
  18. C#应用程序退出后托盘图标(notify…
  19. 2021届前端面试知识点(其他)
  20. 白光干涉仪(光学3D表面轮廓仪)与台阶仪的区别

热门文章

  1. 华为服务器2285怎么做系统,华为服务器2285进bios
  2. 不使用采集卡,实现相机手机多机位直播
  3. 一文读懂VargFacenet
  4. 信捷服务器Z相信号,信捷plc标记与中断处理小知识
  5. TensorFlow2学习七、使用MNIST手写体识别数据集识别自己手写图片
  6. 不限距离4g/5g信号远程遥控小车
  7. 关于H5移动端用什么自动化测试
  8. tinygo的windows环境搭建及简单例程
  9. 【HCIE-RS 天梯路】STP RSTP MSTP
  10. 移动APP卡顿问题解决实践