目录

一、模板的概念

1.1 概念

1.2 特点

二、函数模板

2.1 函数模板

2.2 函数模板注意事项

2.3 函数模板案例

2.4 普通函数与函数模板的区别

2.5 普通函数与函数模板的调用规则

2.6 模板的局限性

三、类模板

3.1 类模板语法

3.2 类模板与函数模板区别

3.3 类模板中成员函数创建时机

3.4 类模板对象做函数参数

3.5 类模板与继承

3.6 类模板成员函数类外实现

3.7 类模板分文件编写

3.8 类模板与友元

四、综合案例


C++ 提高阶段:

本阶段主要针对 C+ + 泛型编程和 STL 技术做详细讲解,探讨 C++ 更深层的使用

一、模板的概念

1.1 概念

模板就是建立通用的模具,大大提高复用性

例如:

(1) 一寸照片模板

(2) PPT 模板

1.2 特点

模板不可以直接使用,它只是个框架

模板的通用并不是万能的

二、函数模板

C++另一种编程思想称为泛型编程,主要利用的技术就是模板

C++提供两种模板机制函数模板和类模板

2.1 函数模板

1、函数模板作用:

建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

2、语法:

template<typename T>
函数声明或定义

3、解释:

template —— 声明创建模板
typename —— 表面其后面的符号是一种数据类型,可以用 class 代替
T —— 通用的数据类型,名称可以替换,通常为大写字母

4、示例:

原来的写法:

void swapInt(int& a, int& b)
{double temp = a;a = b;b = temp;
}void swapDouble(double& a, double& b)
{double temp = a;a = b;b = temp;
}

修改后的写法:

#include<iostream>
using namespace std;
#include<string>template<typename T>void mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void test()
{int a = 10;int b = 20;// 1、自动类型推导mySwap(a, b);cout << "a = " << a << endl;cout << "b = " << b << endl << endl;// 2、显示指定类型mySwap<int>(a, b);cout << "a = " << a << endl;cout << "b = " << b << endl;
}int main()
{test();system("pause");return 0;
}

5、图示

2.2 函数模板注意事项

自动类型推导,必须推导出一致的数据类型 T 才可以使用

模板必须要确定出 T 的数据类型,才可以使用

#include<iostream>
using namespace std;
#include<string>template<class T> // typename可以替换成classvoid mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void test()
{int a = 10;int b = 'b';// 错误:推导不出一致的T类型mySwap(a, b);
}template<class T>
void func()
{cout << "func调用" << endl;
}void test2()
{// 注意:必须要加上 <int>func<int>();
}int main()
{test();test2();system("pause");return 0;
}

2.3 函数模板案例

案例描述:

利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序

排序规则从大到小,排序算法为选择排序

分别利用 char 数组和 int 数组进行测试

#include <iostream>
using namespace std;//交换的函数模板
template<typename T>
void mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}template<class T> // 也可以替换成typename
//利用选择排序,进行对数组从大到小的排序
void mySort(T arr[], int len)
{for (int i = 0; i < len; i++){int max = i; //最大数的下标for (int j = i + 1; j < len; j++){if (arr[max] < arr[j]){max = j;}}if (max != i) //如果最大数的下标不是i,交换两者{mySwap(arr[max], arr[i]);}}
}template<typename T>
void printArray(T arr[], int len) {for (int i = 0; i < len; i++) {cout << arr[i] << " ";}cout << endl;
}void test()
{// 测试 char 数组char charArr[] = "badcfe";int intArr[] = { 7,5,4,1,9,2,3,6,8 };int num = sizeof(charArr) / sizeof(char);mySort(charArr, num);printArray(charArr, num);
}int main()
{test();system("pause");return 0;
}

2.4 普通函数与函数模板的区别

1、普通函数与函数模板区别:

(1) 普通函数调用时可以发生自动类型转换(隐式类型转换)

(2) 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

(3) 如果利用显示指定类型的方式,可以发生隐式类型转换

#include <iostream>
using namespace std;int myAdd01(int a, int b)
{return a + b;
}template<class T>
T myAdd02(T a, T b)
{return a + b;
}void test()
{int a = 10;int b = 20;char c = 'c';cout << myAdd01(a, c) << endl;cout << myAdd02<int>(a, c) << endl;
}int main()
{test();system("pause");return 0;
}

2.5 普通函数与函数模板的调用规则

调用规则如下:

(1) 如果函数模板和普通函数都可以实现,优先调用普通函数

(2) 可以通过空模板参数列表来强制调用函数模板

(3) 函数模板也可以发生重载

(4) 如果函数模板可以产生更好的匹配,优先调用函数模板

1、如果函数模板和普通函数都可以实现,优先调用普通函数

#include <iostream>
using namespace std;void myPrint(int a, int b)
{cout << "调用的是普通函数" << endl;
}template<class T>
void myPrint(T a, T b)
{cout << "调用的模板" << endl;
}void test()
{int a = 10;int b = 20;myPrint(a, b);
}int main()
{test();system("pause");return 0;
}

2、可以通过空模板参数列表来强制调用函数模板

void test()
{int a = 10;int b = 20;myPrint<>(a, b);
}

3、函数模板也可以发生重载

template<class T>
void myPrint(T a, T b, T c)
{cout << "调用的模板" << endl;
}void test()
{int a = 10;int b = 20;myPrint(a, b, 100);
}

2.6 模板的局限性

1、局限性:

模板的通用性并不是万能的

template<class T>
void f(T a, T b)
{a = b;
}

上述代码中提供的赋值操作,如果传入的 a 和 b 是一个数组,就无法实现了

template<class T>
void f(T a, T b)
{if (a > b) { ... }
}

上述代码中,如果传入的数据类型是像 Person 这样的自定义数据类型,也无法正常运行

2、代码

#include <iostream>
using namespace std;
#include <string>class Person
{
public:Person(string name, int age){this->Name = name;this->Age = age;}string Name;int Age;
};template<class T>
bool myCompare(T &a, T &b)
{if (a == b){return true;}else{return false;}
}// 1、运算符重载// 2、具体化Person的版本实现代码 (具体化优先调用)
template<> bool myCompare(Person &p1, Person &p2)
{if (p1.Name == p2.Name && p1.Age == p2.Age){return true;}else{return false;}
}void test01()
{int a = 10;int b = 20;bool ret = myCompare(a, b);if (ret){cout << "a == b" << endl;}else{cout << "a != b" << endl;}
}void test02()
{Person p1("Tom", 10);Person p2("Tom", 11);bool ret = myCompare(p1, p2);if (ret){cout << "p1 == p2" << endl;}else{cout << "p1 != p2" << endl;}
}int main()
{test02();system("pause");return 0;
}

三、类模板

3.1 类模板语法

1、类模板作用:

过一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。

2、代码

#include <iostream>
using namespace std;
#include <string>template<class NameType, class AgeType>
class Person
{
public:Person(NameType name, AgeType age){this->Name = name;this->Age = age;}void showPerson(){cout << Name << " " << Age << endl;}NameType Name;AgeType Age;
};void test()
{Person<string, int> p1("孙悟空", 999);p1.showPerson();
}int main()
{test();system("pause");return 0;
}

3.2 类模板与函数模板区别

1、类模板与函数模板区别主要有两点:

(1) 类模板没有自动类型推导的使用方式

(2) 类模板在模板参数列表中可以有默认参数

2、代码

// 1、类模板没有自动类型推导使用方式
// 即不能使用 Person p1("孙悟空", 999);
Person<string, int> p1("孙悟空", 999);// 2、类模板在模板参数列表中可以有默认参数
template<class NameType, class AgeType = int>Person<string> p2("猪八戒", 999);

3.3 类模板中成员函数创建时机

1、类模板中成员函数和普通类中成员函数创建时机是有区别的:

(1) 普通类中的成员函数一开始就可以创建

(2) 类模板中的成员函数在调用时才创建

2、代码:

#include <iostream>
using namespace std;
#include <string>class Person1
{
public:void showPerson1(){cout << "Person1 show" << endl;}
};class Person2
{
public:void showPerson2(){cout << "Person2 show" << endl;}
};template<class T>
class MyClass
{
public:T obj;// 类模板中的成员函数void func1(){obj.showPerson1();}void func2(){obj.showPerson2();}
};void test()
{MyClass<Person1> m;m.func1();// 下面这个不能运行// m.func2();
}int main()
{test();system("pause");return 0;
}

3、总结:

类模板中的成员函数并不是一开始就创建的, 在调用时才去创建

3.4 类模板对象做函数参数

1、学习目标:

类模板实例化出的对象,向函数传参的方式

2、共有三种传入方式:

(1) 指定传入的类型 —— 直接显示对象的数据类型

(2) 参数模板化 —— 将对象中的参数变为模板进行传递

(3) 整个类模板化 —— 将这个对象类型模板化进行传递

3、代码

#include <iostream>
using namespace std;
#include <string>template<class NameType, class AgeType>
class Person
{
public:Person(NameType name, AgeType age){this->Name = name;this->Age = age;}void showPerson(){cout << Name << " " << Age << endl;}NameType Name;AgeType Age;
};// 1、指定传入类型
void printPerson1(Person<string, int> &p)
{p.showPerson();
}void test1()
{Person<string, int> p("孙悟空", 999);printPerson1(p);
}// 2、参数模板化
template<class NameType, class AgeType>
void printPerson2(Person<NameType, AgeType> &p)
{p.showPerson();cout << "NameType 的类型为:" << typeid(NameType).name() << endl;cout << "AgeType 的类型为:" << typeid(AgeType).name() << endl;
}void test2()
{Person<string, int> p("猪八戒", 999);printPerson2(p);
}// 3、整个类模板化
template<class T>
void printPerson3(T& p)
{p.showPerson();cout << "T 的类型为:" << typeid(T).name() << endl;
}void test3()
{Person<string, int> p("唐僧", 999);printPerson3(p);
}int main()
{test1();test2();test3();system("pause");return 0;
}

4、总结:

通过类模板创建的对象,可以有三种方式向函数中进行传参

使用比较广泛是第一种:指定传入的类型

3.5 类模板与继承

1、当类模板碰到继承时,需要注意一下几点:

(1) 当子类继承的父类是一 个类模板时,子类在声明的时候,要指定出父类中T的类型

(2) 如果不指定,编译器无法给子类分配内存

(3) 如果想灵活指定出父类中T的类型,子类也需变为类模板

2、代码

#include <iostream>
using namespace std;template<class T>
class Base
{
public:T m;
};// 方式一
class Son : public Base<int>
{
};// 方式二:灵活指定父类中 T 类型,子类也需要变类模板
template<class T1, class T2>
class Son2 : public Base<T2>
{T1 obj;
};void test()
{Son s1;Son2<int, char> s2;
}int main()
{test();system("pause");return 0;
}

3.6 类模板成员函数类外实现

1、学习目标:

能够掌握类模板中的成员函数类外实现

2、代码:

#include <iostream>
using namespace std;
#include <string>template<class NameType, class AgeType>
class Person
{
public:Person(NameType name, AgeType age);void showPerson();NameType Name;AgeType Age;
};// 构造函数类外实现
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{this->Name = name;this->Age = age;
}// 成员函数类外实现
template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson()
{cout << Name << " " << Age << endl;
}void test()
{Person<string, int> p("唐僧", 999);p.showPerson();
}int main()
{test();system("pause");return 0;
}

3.7 类模板分文件编写

方法一:

 方法二:

3.8 类模板与友元

1、学习目标:

掌握类模板配合友元函数的类内和类外实现

全局函数类内实现——直接在类内声明友元即可

全局函数类外实现——需要提前让编译器知道全局函数的存在

2、代码

#include <iostream>
using namespace std;
#include <string>// 提前让编译器知道 Person 类存在
template<class NameType, class AgeType>
class Person;// 类外实现
template<class NameType, class AgeType>
void printPerson2(Person<NameType, AgeType> p)
{cout << "类外实现:" << p.Name << " " << p.Age << endl;
}template<class NameType, class AgeType>
class Person
{
public:// 全局函数 类内实现friend void printPerson1(Person<NameType, AgeType> p){cout << "姓名:" << p.Name << " " << " 年龄:" << p.Age << endl;}// 全局函数 类外实现// 加空模板的参数列表// 如果全局函数是类外实现,需要让编译器提前知道这个函数的存在friend void printPerson2<>(Person<NameType, AgeType> p);Person(NameType name, AgeType age){this->Name = name;this->Age = age;}private:NameType Name;AgeType Age;
};// 类内实现测试
void test1()
{Person<string, int> p("Tom", 20);printPerson1(p);
}// 类外实现测试
void test2()
{Person<string, int> p("Tom", 20);printPerson2(p);
}int main()
{test1();test2();system("pause");return 0;
}

3、总结:

建议全局函数做类内实现,用法简单,而且编译器可以直接识别

四、综合案例

4.1 案例描述

1、实现一个通用的数组类,要求如下:

可以对内置数据类型以及自定义数据类型的数据进行存储

将数组中的数据存储到堆区

构造函数中可以传入数组的容量

提供对应的拷贝构造函数以及 operator= 防止浅拷贝问题

提供尾插法和尾删法对数组中的数据进行增加和删除

可以通过下标的方式访问数组中的元素

可以获取数组中当前元素个数和数组的容量

4.2 代码

1、myArry.h 代码

2、

C++学习日记5——模板相关推荐

  1. Vue2源码的学习日记(3)

    组件化 (在文章中的一切源码大部分都是只给出头部,因为源码是开源的(Vue2.6),我认为在自己去边查找边看的过程也能提升到自己) 续学习日记(2)后,便开始慢慢走向vue这个框架的核心,希望能帮到大 ...

  2. 计算机新课标学习心得体会,【精品】新课标学习心得体会模板锦集10篇

    [精品]新课标学习心得体会模板锦集10篇 在平日里,心中难免会有一些新的想法,马上将其记录下来,这样可以记录我们的思想活动.很多人都十分头疼怎么写一篇精彩的心得体会,下面是小编收集整理的新课标学习心得 ...

  3. 智能车学习日记【一】——让小车跑正方形赛道(摄像头图像处理赛道)

    智能车学习日记[一]--让小车跑正方形赛道 目录 开篇 舵机 赛道图像处理 图像处理 代码![在这里插入图片描述](https://img-blog.csdnimg.cn/9ec0eb76bd8941 ...

  4. OpenGL蓝宝书学习日记(1)—— 配置OpenGL环境与创造第一个三角形

    OpenGL蓝宝书学习日记(1)-- 配置OpenGL环境与创造第一个三角形 一.安装VS VS有众多版本,本人使用的是VS2017,在官网即可下载,有为学生专门提供的免费版,注册账号登陆后即可无限试 ...

  5. vivado高层次综合(high-level synthesis,HLS)学习日记

    一.前言        架不住老大的淫威,本作者很不情愿的开始了HLS学习,这篇学习日记实际是重新表述文件1(详见后面的参考列表)中的一些重点内容.我认为高层次综合还是没有纯verilog来的爽,虽然 ...

  6. 【前端学习日记】利用reveal.js把实验报告做成一个简单的幻灯片

    一.整体效果 把电磁场的实验报告做成网页PPT,原文是这里:<[电磁场实验作业]有限差分法(FDM)求解静电场电位分布_轩辕衍的博客-CSDN博客> 二.核心代码讲解 0.创建页面 第一篇 ...

  7. java的圆周率_java学习日记,圆周率的打印

    前段时间看到听说学习java每天写技术贴会对自己提升很大,我现在学习java也就2个周,算不上技术贴,就写写学习日记吧. 昨天师傅给我出了一道题,说是试试用java打印圆周率. 刚开始我的思路是,如果 ...

  8. GPU(CUDA)学习日记(十一)------ 深入理解CUDA线程层次以及关于设置线程数的思考

    GPU(CUDA)学习日记(十一)------ 深入理解CUDA线程层次以及关于设置线程数的思考 标签: cuda存储线程结构网格 2012-12-07 16:30 6298人阅读 评论(4)收藏 举 ...

  9. GPU(CUDA)学习日记(十三)------ CUDA内存简介

    GPU(CUDA)学习日记(十三)------ CUDA内存简介 标签: cuda存储线程结构 2012-12-07 16:53 2902人阅读 评论(0)收藏 举报 分类: GPU(16) CUDA ...

  10. GPU(CUDA)学习日记(九)------ CUDA存储器模型

    GPU(CUDA)学习日记(九)------ CUDA存储器模型 标签: cuda存储bindingcache编程api 2012-09-27 10:53 1677人阅读 评论(1) 收藏 举报 分类 ...

最新文章

  1. Python——字符串大小写转化
  2. springboot超详细教程_全网最细致的SpringBoot实战教程,超适合新手小白入坑学习...
  3. tornado(七)
  4. activemq高级客户端选项
  5. jquery ajax跨域访问webservice配置
  6. jpa 分页 排序 过滤_使用JPA标准@ViewScoped通过分页,过滤和排序进行Primefaces DataTable延迟加载...
  7. oracle   SQL执行过程
  8. 达梦数据charindex_更新日志 · dotnetcore/FreeSql Wiki · GitHub
  9. dicom文件的后缀_dcm文件扩展名,dcm文件怎么打开?
  10. #百度云直链下载-IDM+油猴插件
  11. 商用密码产品认证-智能密码钥匙
  12. JavaScript编程精解(笔记1)
  13. 数据结构严蔚敏--综述
  14. mysql rpl_mysql5.5 半同步参数rpl_semi_sync_master_timeout 测试解决办法
  15. Android逆向:smali编码实践(三)—— 实体类创建以及if判断
  16. 第六十七章 方法关键字 - Language
  17. 机器学习基础、sklearn数据集、转换器与预估器
  18. 数据库SQL优化大总结
  19. linux rmvb转mp4,linux系统下实现播放rmvb文件
  20. 精讲响应式WebClient第3篇-POST、DELETE、PUT方法使用

热门文章

  1. 2012年3月份第2周51Aspx源码发布详情
  2. 通达信版弘历软件指标_[转载]弘历软件指标源码
  3. python爬取起点中文网小说
  4. c语言中文内码,用C语言实现常见的三种中文内码转换
  5. SPSS:因子分析步骤
  6. 博士申请 | 澳大利亚麦考瑞大学吴佳教授招收图挖掘方向全奖博士生
  7. 华为安全HCIP-Security H12-721、H12-722、H12-723题库,含三套vce软件
  8. 适用mac微信用户的免登陆多开防撤回插件-WeChatTweak
  9. 只用一招!Python实现微信防撤回!
  10. 计算机考试感受作文,关于考试后的感想作文(精选10篇)