• C++:模板(初级)


    hello,各位小伙伴,本篇文章跟大家一起学习《C++:模板(初级)》,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 !
    如果本篇文章对你有帮助,还请各位点点赞!!!
    在这里插入图片描述

    话不多说,开始进入正题

    🚀 认识模板

    1.✈️ 模板的定义和作用

    我们知道再制造月饼或其它食物时,会用到模具
    在这里插入图片描述
    可以用来快速制作食品样子,即使食品的材料不一样,但是造出来的样子都是大差不差的。

    同理,C++中的模板也是用来制造的,我们来看下述代码:

    void Swap(int& left, int& right)
    {
    	int temp = left;
    	left = right;
    	right = temp;
    }
    
    void Swap(double& left, double& right)
    {
    	double temp = left;
    	left = right;
    	right = temp;
    }
    
    void Swap(char& left, char& right)
    {
    	char temp = left;
    	left = right;
    	right = temp;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    以上代码都是实现了交换的功能,利用了函数重载,但是要交换不同的类型,就要再写一次函数,那实在是太麻烦了,所以引入了模板。以交换功能为主,实现多种类型的交换,也就是:“交换功能”为模板,“交换类型”为材料,我们来提供材料,编译器负责用模板制造。

    🚀 模板

    1.✈️ 函数模板

    🔥函数模板的概念

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

    🔥模板格式:

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

    注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
    举个例子:

    template <class T>
    void Swap(T& x, T& y)
    {
    	T tmp;
    	tmp = x;
    	x = y;
    	y = tmp;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    🔥模板的原理:
    编译器是否是调用了该模板函数来实现交换功能呢?实际上并不是,就好像你用月饼摸具来制造月饼,但是我们吃的还是月饼,也就是说,模板函数只是帮我们制造函数,并不会帮我们实现功能。在这里插入图片描述
    在实际调用模板函数时,编译器会帮我们制造该类型的函数,然后再调用被制造的函数。
    那编译器怎么知道我们要那种呢?这就和函数重载有点像,编译器会通过对实参的推演,确定类型,产生一份专门属于该类型的代码。如:

    template <class T>
    void Swap(T& x, T& y)
    {
    	T tmp;
    	tmp = x;
    	x = y;
    	y = tmp;
    }
    
    int main()
    {
    	int a = 10;
    	int b = 20;
    
    	cout << "a = "<< a << " " << "b = " << b << endl;
    	Swap(a, b);
    	cout << "a = " << a << " " << "b = " << b << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    2.✈️函数模板的实例化

    那么我要交换int类型和double类型,可以这么写:

    template <class T1,class T2>
    void Swap(T1& x, T2& y)
    {
    	T1 tmp;
    	tmp = x;
    	x = y;
    	y = tmp;
    }
    
    int main()
    {
    	int a = 1;
    	double b = 2.3;
    	cout << "a = " << a << " " << "b = " << b << endl;
    	Swap(a, b);
    	cout << "a = " << a << " " << "b = " << b << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    但是发现精度丢失了在这里插入图片描述
    我们再来看:

    template <class T>
    T Add(const T& left, const T& right)
    {
    	return left + right;
    }
    
    int main()
    {
    	int a = 1;
    	double b = 2.3;
    	cout << "a = " << a << " " << "b = " << b << endl;
    	Add(a, b);
    	cout << "a = " << a << " " << "b = " << b << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    会发现编译器会报错,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型 通过实参a将T推演为int,通过实参b将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错
    注意:在模板中,编译器一般不会进行类型转换操作

    🔥这时候就要用到函数模板的实例化
    用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化

    • 1.隐式实例化:
    template<class T>
    T Add(const T& left, const T& right)
    {
    	return left + right;
    }
    
    int main()
    {
    	int a1 = 10, a2 = 20;
    	double d1 = 10.0, d2 = 20.0;
    	Add(a1, a2);
    	Add(d1, d2);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 2.显式实例化:在函数名后的<>中指定模板参数的实际类型
    int main(void)
    {
    	int a = 10;
    	double b = 20.0;
    
    	// 显式实例化
    	Add<int>(a, b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

    3.✈️模板参数的匹配原则

    1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
    // 专门处理int的加法函数
    int Add(int left, int right)
    {
    	return left + right;
    }
    
    // 通用加法函数
    template<class T>
    T Add(T left, T right)
    {
    	return left + right;
    }
    
    void Test()
    {
    	Add(1, 2); // 与非模板函数匹配,编译器不需要特化
    	Add<int>(1, 2); // 调用编译器特化的Add版本
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    1. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
    // 专门处理int的加法函数
    int Add(int left, int right)
    {
    	return left + right;
    }
    
    // 通用加法函数
    template<class T1, class T2>
    T1 Add(T1 left, T2 right)
    {
    	return left + right;
    }
    
    void Test()
    {
    	Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
    	
    	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,
    				//编译器根据实参生成更加匹配的Add函数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

    🚀 类模板

    1.✈️类模板的定义格式

    template<class T1, class T2, ..., class Tn>
    class 类模板名
    {
    	// 类内成员定义
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    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();
    		// ...
    
    		size_t Size() { return _size; }
    
    	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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    2.✈️类模板的实例化

    类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

    // Vector类名,Vector才是类型
     Vector<int> s1;
     Vector<double> s2;
    
    • 1
    • 2
    • 3

    你学会了吗?
    好啦,本章对于《C++:模板(初级)》的学习就先到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!

    如你喜欢,点点赞就是对我的支持,感谢感谢!!!

    请添加图片描述

  • 相关阅读:
    OAuth2:资源服务器
    PLSQL调整SQL字体大小
    【 java 面向对象之 Object 类】toString() 方法的重写
    Linux基础知识与实操-篇五:bash使用进阶
    看完这篇原型设计模式,还不会,请你吃瓜
    关于Fragment的生命周期,你知道多少?
    Jackson自定义序列化
    网工内推 | 国企,解决方案工程师,最高30k,有软考证书优先
    【Java】抽象类和接口
    JVM参数配置 JDK1.8
  • 原文地址:https://blog.csdn.net/2301_80153885/article/details/138167727