IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 模板初阶<C++初阶>(跑路人笔记) -> 正文阅读

[C++知识库]模板初阶<C++初阶>(跑路人笔记)

前言

主要讲述一下模板怎么用,以及注意事项让我们可以使用模板,后续细节会在模板进阶的时候讲到.

先用一下模板给大家看看吧.

template<typename T>
void Swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

我们这个Swap函数就可以完成任何类型的调用(前提是a,b类型相同–原因后面会讲).如我们传两个int两个double我们的Swap函数都可以完美运行.来看看吧

image-20220531184425572

这个功能的实现其实也是很简单的我们的编译器会根据传来的类型生成一个对应类型的函数,我们先简单看一下VS的反汇编代码吧.

image-20220531185322959

image-20220531185318180

看上面两个函数的指令我们就可以看出我们的Swap函数生产了两个.

而这就是模板的主要功能:

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定 类型版本.

也就是说我们的模板可以无视类型来帮你生成一个相对应类型的函数.

很像auto关键字,但是auto无法作为函数参数,而我们的模板用于函数.

但是模板有他的使用规则.

模板

函数模板

模板使用:

template<typename T1, typename T2,......,typename Tn>
  TN       函数名(TN ..,T. .. , ...){}
//返回值类型 函数名(参数列表){}

image-20220601095930701

注意: 我们每创建一个模板函数都要在其上方写template<….>两者必须对应.

我们再看看我们上面的例子

template<class T>
void Swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

如果我们还想再创建一个模板函数

如下

image-20220601100527244

看右图我们的错误演示和左图的正确演示就可以看出了.我们每个模板函数都要与template<..>对应.

模板声明问题

我们大部分人呢,习惯于将声明放在.h的头文件里,把函数主体放在.cpp中再把main函数单独放在另一个.cpp文件中,但是模板函数不允许我们这样做.

来看看问题吧

结构如下(注:声明也是需要加上template<……>)

image-20220601101923178

发生错误如下,这些是连接错误,其实原因也很简单

image-20220601101947864

我们的test.cpp因为没办法接收到参数,所以test.cpp也就没办法在创建符号表的时候创建出对应名称的连接处,所以我们的main.cpp部分的文件其实就只是得到了一个声明仅此而已.

类似下面代码,报错也相同.

image-20220601103136933

毕竟我们的头文件会展开的=.=

问题解决

所以我们如果非要声明让模板部分和main函数部分分隔开的话,我们建议是使用.hpp为后缀的头文件并把模板函数定义和声明都放在.hpp后缀的文件内.

image-20220601104354476

其实原因也很简单因为我们的.hpp会在main.cpp哪里展开所以其实也就类似于我们没有分开.

但是如果还是想分成.h .cpp .cpp 的格式的话.

我们就可以选择实例化指定.

如下图:我们虽然还是.h .cpp .cpp的格式但是却可以正常运行

image-20220601111541585

这样我们的test.cpp部分函数会因为下面的实例化指定在汇编的时候生成对应声明的实例化函数.

来看看linux的反汇编是否和我们推理相同呢?

image-20220601113647229

是的通过objdump工具我可以看出我们的两种不同Swap函数(注:模板生成的函数的反汇编名字和普通函数反汇编名有所不同.)

(ps:如果想自己操作的话请转至[linux操作](# linux操作))


类模板

结构如下:

template<class T1, class T2, ..., class Tn>
class 类模板名
{
 // 类内成员定义
}; 

事例如下:

// 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{
public:
	Vector(size_t capacity = 10)
		: _pData(new T[capacity])
		, _size(0)
		, _capacity(capacity)
	{}
	// 使用析构函数演示:在类中声明,在类外定义。
	~Vector();
	void PushBack(const T& data)void PopBack()// ...
	T& operator[](size_t pos)
	{
		assert(pos < _size);
		return _pData[pos];
	}
private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};
// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{
	if (_pData)
		delete[] _pData;
	_size = _capacity = 0;
}

注意我们上面的类只是一个模板我们实例化格式如下

// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

其实在使用的时候也就是在类名和对象名之间加个<类型>.

模板使用实例

先看看函数模板的使用吧.

如果我们想两个不同类型的做函数形参要这么办呢?

很简单如下:

我们可以创建个两个模板参数的模板.

#include<iostream>
using namespace std;
template <typename T1, typename T2>
void Test(T1 a, T2 b)
{
	return;
}
int main()
{
	Test(1, 1.0);
	return 0;
}

模板参数也可以当做函数的返回值.

而且我们可以给我们的模板参数赋予缺省值

赋值方式如下

template <typename T1 = char, typename T2 = double>

我们也可以在使用的时候显示实例化来创建一个指定类型的模板实例化.

#include<iostream>
using namespace std;
template <typename T1 = char, typename T2 = double>
void Test(T1 a, T2 b)
{
	return;
}
int main()
{
	Test<int, double>(1, 1.0);//其中<>里的两个类型会分别传给T1和T2
	return 0;
}

如果我们定义了一个和模板功能名称相同的函数会发生什么呢?

代码如下

#include<iostream>
using namespace std;
int Add(int a, int b)
{
	return a + b;
}
template <typename T>
T Add(T& a, T& b)
{
	return a + b;
}
int main()
{
	int a = 1;
	int b = 2;
	Add<int>(a, b);
	Add(a, b);
	return 0;
}

让我们到linux里看看

image-20220601151214375

虽然在我们的印象里他们是相同的但是其实在反汇编后的命名还是不相同的.所以两者可以共存.

linux操作

创建两个文件一个是test.cpp 一个是main.cpp 然后把代码放入

//test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
template<class T>
void Swap(T& a, T& b);
template<class T>
void Swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
template
void Swap<int>(int& a, int& b);
template
void Swap<double>(double& a, double& b);
//main.cpp
#include<iostream>
using namespace std;
template<class T>
void Swap(T& a, T& b);
int main()
{
	int a = 1;
	int b = 2;
	Swap(a, b);
	double c = 1.2;
	double d = 2.1;
	Swap(c, d);
	cout << "a>:  " << a << endl << "b>:  " << b << endl << "c>:  " << c << endl << "d>:  " << d << endl;
	return 0;
}

保存退出后

在命令行分别写下如下指令

g++ test.cpp main.cpp -o TEST

objdump -S TEST

就可以查找到Swap的两个函数了

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章           查看所有文章
加:2022-06-08 18:52:05  更:2022-06-08 18:52:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:34:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码