C++函数模板初阶

本文介绍了C++中的函数模板,作为泛型编程的一种方式,它允许编写与类型无关的通用代码。文章详细讲解了函数模板的概念、格式、工作原理、隐式和显示实例化,以及模板参数的匹配原则。此外,还探讨了类模板的格式和实例化过程。
摘要由CSDN通过智能技术生成
  1. 泛型编程

    编写与类型无关的通用代码,是代码复用的一种手段

    在实现swap函数的时候,我们可以利用函数重载来实现两个数字之间的互换,代码演示:

    #include<iostream>
    
    using std::cout;
    using std::endl;
    
    void swap(int& a, int& b)
    {
    	int tmp = a;
    	a = b;
    	b = tmp;
    }
    
    void swap(double& a, double& b)
    {
    	double tmp = a;
    	a = b;
    	b = tmp;
    }
    
    int main()
    {
    	int a = 0;
    	int b = 1;
    	swap(a, b);
    	cout << a << endl;
    	cout << b << endl;
    
    	double c = 0.1;
    	double d = 0.2;
    	swap(c, d);
    	cout << c << endl;
    	cout << d << endl;
    }
    

    输出结果为:
    在这里插入图片描述
    注意:上述代码虽然利用函数重载避免了取函数名的麻烦,但是重载的函数仅仅只是类型不同,代码的复用率比较低,只要有新类型出现时,就需要增加对应的函数 ,那么我们可不可以使用泛型编程的手段来解决这类问题?此时就提出了函数模板

  2. 函数模板

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

    2.1函数模板格式

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

    代码演示:

    #include<iostream>
    
    using std::cout;
    using std::endl;
    
    template<typename T>  //也可以写成这样:template<class T>
    void swap(T& left, T& right)
    {
    	T tmp = left;
    	left = right;
    	right = tmp;
    }
    
    int main()
    {
    	int a = 0;
    	int b = 1;
    	swap(a, b);
    	cout << a << endl;
    	cout << b << endl;
    
    	double c = 0.1;
    	double d = 0.2;
    	swap(c, d);
    	cout << c << endl;
    	cout << d << endl;
    }
    

    输出结果为:
    在这里插入图片描述
    注意::typename是用来定义模板参数关键字,也可以使用class来代替,但是不能用struct来代替class

    2.2函数模板原理

    函数模板本身并不是一个函数,可以说它是一个拓印工具,我们将它交给编译器,编译器根据实参的数据类型来推测出形参的数据类型,然后再实例化成一个具体的函数

    如图所示:
    在这里插入图片描述
    注意: 在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供 调用,比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然 后产生一份专门处理double类型的代码

    2.4函数模板实例化

    2.4.1隐式实例化

    让编译器根据实参推演模板参数的实际类型

    格式:函数名 <模板参数的实际类型>()

    注意:上面的所有代码都是隐式实例化,通过实参来推演模板参数的实际类型

    2.4.2显示实例化

    当编译器无法通过实参推出模板参数类型,或者根本没有实参的时候,需要用到显示实例化

    代码演示:

    #include<iostream>
    
    using std::cout;
    using std::endl;
    
    template<class T>
    T Add(const T& left,const T& right)
    {
    	return left+right;
    }
    
    void founc()
    {
    	T tmp;
    	cout<<tmp<<endl;
    	//不考虑没定义tmp报的错误
    }
    
    int main()
    {
    	Add(1,1.5);
    	//此语句会报错,因为再利用实参推模板参数的时候,由于模板参数的类型只有一个T,所以无法推出两个类型
    	//为了解决这个问题,我们可以自己强制类型转换,将1的类型int转为double,也可以使用显式类型转换,如下语句:
    	Add<double>(1,1.5);
    	//当没有实参的时候也必须使用显示实例化,如:
    	founc();
    	//此语句会报错,因为没有实参,模板无法推出T是什么类型,通过显示实例化改为如下语句:
    	founc<int>();
    	return 0;
    }
    

    2.5模板参数的匹配原则

    1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

    代码演示:

    #include<iostream>
    
    using namespace std;
    
    //专门处理int相加的函数
    int Add(int left,int right)
    {
    	return left+right;
    }
    
    //模板相加函数
    template <class T>
    T Add(T left,T right)
    {
    	return left+right;
    }
    
    int main()
    {
    	Add(1,2);//与专门处理int相加的函数匹配,不需要模板实例化
    	Add<int>(1,2);//与模板相加函数匹配,需要模板显示实例化
    }
    
    1. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

    代码演示:

    #include<iostream>
    
    using namespace std;
    
    //专门处理int相加的函数
    int Add(int left,int right)
    {
    	return left+right;
    }
    
    //模板相加函数
    template <class T1,class T2>
    T Add(T1 left,T2 right)
    {
    	return left+right;
    }
    
    int main()
    {
    	Add(1,2);//与专门处理int相加的函数匹配,不需要模板实例化
    	Add(1,2.1);//与模板相加函数匹配,需要模板隐式实例化
    }
    
    
    
  3. 类模板

    3.1类模板格式:

    template<class T>
    class 类模板名
    {
    	//类内成员定义
    }
    

    代码演示:

    //以顺序表为例
    template<class T>
    class Seqlist
    {
    public:
    	Seqlist()//构造函数
    	{
    		_arr = new T[10];
    		_size = 0;
    		_capacity = 0;
    	}
    	~Seqlist()//析构函数
    	{
    		delete[] _arr;
    	}
    	Seqlist(const Seqlist<T>& data)//拷贝构造
    	{
    		_arr = data._arr;
    		_size = data._size;
    		_capacity = data._capacity;
    	}
    	Seqlist<T>& operator=(const Seqlist<T>& data)//赋值运算符重载
    	{
    		_arr = data._arr;
    		_size = data.size;
    		_capacity = data._capacity;
    	}
    	void PushBack(const T& data)//尾插
    	{
    		//不考虑增容问题
    		_arr[_size] = data;
    		_size++;
    	}
    	void PopBack()//尾删
    	{
    		if (_size > 0)
    			--_size;
    	}
    	void Print()//打印
    	{
    		for (size_t i = 0; i < _size; ++i)
    			cout << _arr[i]<<" ";
    		cout << endl;
    	}
    private:
    	T* _arr;
    	size_t _size;
    	size_t _capacity;
    };
    

    3.2类模板实例化

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

    代码演示(利用3.1的顺序表的类模板):

    #include<iostream>
    
    int main()
    {
    	Seqlist<int> s1;
    	Seqlist<double>s2;
    	s1.PushBack(1);
    	s1.PushBack(2);
    	s1.PushBack(3);
    	s1.Print();
    	s2.PushBack(1.1);
    	s2.PushBack(2.2);
    	s2.PushBack(3.3);
    	s2.Print();
    }
    

    输出结果为:
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值