【C++深入浅出】模版初识

这篇具有很好参考价值的文章主要介绍了【C++深入浅出】模版初识。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 


目录

一. 前言

二. 泛型编程

三. 函数模版 

3.1 函数模版的概念

3.2 函数模版的格式

3.3 函数模版的原理

3.4 函数模板的实例化

3.5 模板参数的匹配原则

四. 类模版

4.1 类模版的定义

4.2 类模版的实例化


一. 前言

        本期我们要介绍的是C++的又一大重要功能----模版。通过模版,我们可以很轻松的进行泛型编程,大大简化我们编程时的代码。

        本文的目标是让读者对模版有一定程度上的了解,以便后续STL的学习,对于模版更深层次的内容,我们放到以后再进行拓展。

        话不多说,开启我们今日的学习叭【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程

二. 泛型编程

        假如现在有个需求,要求我们实现一个swap函数用于数据的交换,数据类型可能是整形浮点型字符型等等,通过我们之前学习的知识,你会如何进行实现呢?

        聪明的你可能会这样实现【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程

//利用函数重载实现三种不同类型的swap函数
void Swap(int& x, int& y) //引用传参
{
	int tmp = x;
	x = y;
	y = tmp;
}

void Swap(double& x, double& y)
{
	double tmp = x;
	x = y;
	y = tmp;
}

void Swap(char& x, char& y)
{
	double tmp = x;
	x = y;
	y = tmp;
}

        是的,上面的代码确实实现了我们的需求,但还是存在一些不足,体现在如下两个方面:

  1. 几个重载函数仅仅是类型不同,代码逻辑都是一样的,代码的复用率比较低。每当有新类型出现时,用户就需要再自行添加一个函数
  2. 代码的可维护性比较低,一个出错可能所有的重载均出错(想必你写的时候也是CV的叭【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程)

        依照以上两点不足,那我们能不能只给编译器提供一个模板,到时候让编译器根据我们指定的类型自动生成所需要的函数呢?

        答案是有的。C++为我们提供了模版的概念,支持我们进行泛型编程。模版就好比一个模具,我们可以从倒入任何种类(类型)的东西到模具中,最终都会形成我们想要的对应图案(即生成相应的代码)

        泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模版是泛型编程的基础。模板分为函数模版类模版

【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程

三. 函数模版 

3.1 函数模版的概念

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

3.2 函数模版的格式

        函数模版的定义格式如下所示:

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

        例如,我们将上面的swap函数定义为函数模版如下:

template<typename T> //T是类型名
void swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

其中,typename是定义模版参数的关键字,也可以使用class关键字替代。

3.3 函数模版的原理

        我们可以把函数模板看做一个蓝图,它本身并不是一个函数,而是编译器根据调用函数时传入实参产生特定类型函数的一个模具。

        例如,当我们给swap函数传入double类型的参数,编译器就会自动根据模板生成一份参数为double类型的swap函数;而如果我们传入的是int类型的参数,编译器就会生成一份参数为int类型的swap函数。

        由此可见,模板就是将本来应该我们做的重复的事情交给了编译器去做,将手动敲出来的代码变为编译器自动生成。

【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程

在编译器编译阶段时,对于模板函数的使用,编译器需要根据传入的实参类型来推演T并生成对应类型的函数以供调用。比如:当double类型数据使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于其余类型也是如此。

3.4 函数模板的实例化

        当不同类型的参数使用函数模板时,编译器根据模版为这个类型生成一份函数代码,这个过程称为函数模板的实例化。模板参数实例化分为:隐式实例化显式实例化

        隐式实例化

        即编译器根据实参的类型自动推导模版参数的类型:

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

int main()
{
	double d1 = 2.0;
	double d2 = 5.0;
	Add(d1, d2); //d1和d2是double类型的,编译时编译器推导T为double类型,实例化double类型的Add函数

	int i1 = 0;
	Add(i1, d2); //此处编译会报错,因为Add只有一个模板参数T,而实参有两种类型,编译器推导时不知道将T推导为int还是double
	return 0;
}

         可以看到,编译器进行隐式实例化也不是胡乱实例化的,必须要有唯一的结果编译器才会进行实例化,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅。

        要解决上面Add函数的问题,我们有两个解决方法:

        1、用户自行进行强制类型转换

Add(i1, (int)d2); //将两个实参都变为int类型

Add((double)i1, d2); //将两个实参都变为double类型

        2、使用下面的显式实例化【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程

        显式实例化

        调用函数时我们可以在函数名后使用<>来指定模板参数的实际类型,如下:【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程

int main()
{
	int a = 10;
	double b = 20.0;
	double c = 30.0;
	// 显式实例化
	Add<int>(a, b);  //显式指定T实例化为int
	Add<double>(b, c);  //显式指定T实例化为double
	return 0;
}

        当实例化的函数形参和实参的类型不匹配时,编译器就会尝试进行隐式类型转换,如果无法转换成功编译器将会报错【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程

template<typename T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int i = 10;
	double d = 10.0;
	Swap<int>(i, d); //尽管我们显式进行实例化了,但这里编译器仍然会报错,原因是形参和实参无法进行隐式类型转换
	return 0;
}

【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程

3.5 模板参数的匹配原则

        1、 一个非模板函数可以和一个同名的函数模板同时存在,并且该函数模板还可以被实例化为这个非模板函数。换句话说,我们既可以使用非模板函数,也可以使用编译器特化的模板函数。

//Swap函数模板
template<class T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

//处理int类型的Swap函数
void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 10, b = 20;
	Swap(a, b); //使用下面专门处理int的Swap函数
	Swap<int>(a, b); //使用上面编译器特化后的Swap函数
	return 0;
}

        2、对于非模板函数和同名函数模板,调用时会调用最匹配的那个函数。在其他条件相同的情况下,编译器会优先调用非模板函数。如果模板可以产生一个更匹配的函数,则将选择模板。
 

//Add函数模板
template<class T1,class T2>
T1 Add(T1 x, T2 y)
{
	return x + y;
}

//处理int类型的Add函数
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	Add(10, 20); //虽然可以通过模板生成<int,int>的模板函数,但编译器并不会去生成,而是去调用已经存在的非模板函数
	Add(10, 20.0); //通过函数模板生成的函数会更加匹配,故调用函数模版生成的Add函数
	return 0;
}

         3、模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

//Add函数模板
template<class T>
T Add(T x, T y)
{
	return x + y;
}

int Swap(int x, int y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 20;
	double d = 10.0;
	Add(d, a); //这里会报错,Add是模板函数,不支持自动类型转换
	Swap(d, a); //这里编译通过,d会发生隐式类型转换为int类型
	return 0;
}

【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程


四. 类模版

4.1 类模版的定义

        在C++中,除了普通函数支持模版,类也支持模版,我们把这样的类称为类模版。下面给出一个类模版的定义格式:

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

        下面我们用vector类来演示一下类模板的定义方法以及注意事项(只是演示噢,并不是真正的vector模拟实现)【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程

//vector类
template<class T>
class Vector
{
public:
    //构造时使用new动态申请空间
	Vector(int capacity = 10)
		:_p(new T[capacity])
		_capacity(capacity)
		,_size(0)
	{}

	void puch_back(const T& x); //尾插
	void pop_back(); //尾删

	~Vector(); //析构时需要调用delete释放空间
private:
	T* _p;
	int _capacity;
	int _size;
};

//在类模板外定义成员函数,需要加上模版参数列表
template<class T>
Vector<T>::~Vector()
{
	if (_p != nullptr)
	{
		delete[] _p;
		_capacity = _size = 0;
	}
}

值得注意的是:和模板函数一样,类模板并不是一个具体的类,它只是一个模具,只有进行实例化后才会变成一个具体的类。

4.2 类模版的实例化

        与函数模板实例化不同,类模板不支持隐式类型推导,类模板实例化需要在类模板名字后用<>显式指定模板参数的实际类型。注意:类模板的名字不是真正的类,只有指定类型实例化后才是真正的类。

//模板的实例化
int main()
{
    //Vector是类名,Vector<int>才是类型名,才能用来定义对象
	Vector<int> vi(20); //用参数为int的Vector类定义对象
	Vector<double> vd(10); //用参数为double的Vector类定义对象
	return 0;
}

        类模版经过类型实例化后得到的类我们称作模板类,通过模板类我们就可以定义许多相应的类对象,三者的关系可用下图表示: 

【C++深入浅出】模版初识,C++深入浅出,c++,函数模板,类模板,泛型编程


 以上,就是本期的全部内容啦🌸

制作不易,能否点个赞再走呢🙏文章来源地址https://www.toymoban.com/news/detail-716313.html

到了这里,关于【C++深入浅出】模版初识的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++初阶】八、初识模板(泛型编程、函数模板、类模板)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【C++初阶】七、内存管理 (C/C++内存分布、C++内存管理方式、operator new / delete 函数、定位new表

    2024年02月04日
    浏览(45)
  • 【C++深入浅出】类和对象中篇(六种默认成员函数、运算符重载)

    目录 一. 前言  二. 默认成员函数 三. 构造函数 3.1 概念 3.2 特性 四. 析构函数 4.1 概念 4.2 特性 五. 拷贝构造函数 5.1 概念 5.2 特性 六. 运算符重载 6.1 引入 6.2 概念 6.3 注意事项 6.4 重载示例 6.5 赋值运算符重载 6.6 前置++和后置++运算符重载 七. const成员函数 7.1 问题引入 7.2 定义

    2024年02月09日
    浏览(63)
  • 【深入浅出C#】章节 5: 高级面向对象编程:泛型编程和集合类型

    高级面向对象编程是在基础面向对象编程的基础上进一步深入和拓展的一种编程范式。它强调封装、继承和多态的概念,并引入了泛型编程和集合类型等高级特性。高级面向对象编程提供了更灵活、可扩展和可复用的代码结构,能够帮助开发者构建更复杂、更高效的应用程序

    2024年02月16日
    浏览(61)
  • 深入浅出C语言—【函数】下

    函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。 注意: 函数可以嵌套调用,但是不能嵌套定义。 把一个函数的返回值作为另外一个函数的参数。 上面的strlen函数是求数组长度的库函数, 特别注意的是,当数组为字符数组时,数组的末尾会自动放一个

    2024年02月17日
    浏览(82)
  • 深入浅出C语言—【函数】上

       目录 1.函数的概念 2.C语言函数的分类 2.1 库函数 2.1.1 strcpy库函数举例学习方式 2.1.2 库函数扩展知识 2.2 自定义函数 2.2.1求两个整数中的较大值 3. 函数的参数 3.1 实际参数(实参) 3.2 形式参数(形参) 4. 函数的调用 4.1 传值调用 4.2 传址调用 老铁们,网址自取,记得一键

    2024年02月07日
    浏览(77)
  • 深入浅出C++ ——线程库

      在C++11之前,涉及到多线程问题,都是和平台相关的,比如windows和linux下各有自己的接口,这使得代码的可移植性比较差。C++11中最重要的特性就是对线程进行支持了,使得C++在并行编程时不需要依赖第三方库,而且在原子操作中还引入了 原子类 的概念。要使用标准库中

    2024年02月03日
    浏览(70)
  • 深入浅出C++——C++的类型转换

    在C语言中,如果 赋值运算符左右两侧类型不同 ,或者形参与实参类型不匹配,或者 返回值类型与接收返回值类型不一致 时,就需要发生类型转化。 C语言中总共有两种形式的类型转换: 隐式类型转换:编译器在编译阶段自动进行转换,不能转就编译失败。 显式类型转换:

    2024年02月07日
    浏览(56)
  • 深入浅出Pytorch函数——torch.sum

    分类目录:《深入浅出Pytorch函数》总目录 相关文章: · 深入浅出TensorFlow2函数——tf.reduce_sum · 深入浅出TensorFlow2函数——tf.math.reduce_sum · 深入浅出Pytorch函数——torch.sum · 深入浅出PaddlePaddle函数——paddle.sum 语法 参数 input :[ Tensor ] 输入的张量。 dim :[可选, int / tuple ] 求和

    2024年02月04日
    浏览(80)
  • 深入浅出Pytorch函数——torch.full

    分类目录:《深入浅出Pytorch函数》总目录 相关文章: · 深入浅出Pytorch函数——torch.Tensor · 深入浅出Pytorch函数——torch.ones · 深入浅出Pytorch函数——torch.zeros · 深入浅出Pytorch函数——torch.full · 深入浅出Pytorch函数——torch.ones_like · 深入浅出Pytorch函数——torch.zeros_like · 深

    2024年02月07日
    浏览(64)
  • 深入浅出Pytorch函数——torch.tensor

    分类目录:《深入浅出Pytorch函数》总目录 相关文章: · 深入浅出TensorFlow2函数——tf.constant · 深入浅出Pytorch函数——torch.tensor · 深入浅出Pytorch函数——torch.as_tensor · 深入浅出Pytorch函数——torch.Tensor · 深入浅出PaddlePaddle函数——paddle.to_tensor 基于 data 构建一个没有梯度历史

    2024年02月04日
    浏览(110)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包