Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!

我的博客:<但凡.

我的专栏:《编程之路》《数据结构与算法之美》《题海拾贝》《C++修炼之路》

欢迎点赞,关注!

目录

 

1、泛型编程 

2、函数模板

2.1、模板使用

2.2、模板样式:

2.3、函数模板实例化

2.3.1、隐式实例化

2.3.2、显式实例化

 2.4、模板参数匹配规则

3、类模板

 

1、泛型编程 

        首先我们来看下面一个场景:

int Add(int x, int y)
{
	return x + y;
}
double Add(double x, double y)
{
	return x + y;
}
char Add(char x, char y)
{
	return x + y;
}

        我们实现了三个加法函数,因为有函数重载的支持,我们在调用函数的时候会自动调用匹配的函数,但是这未免也太麻烦了吧?要写这么多重复的代码,那么有没有一种方式,告诉编译器一个模子,让编译器自动生成这些函数呢?

        C++中是支持这种“模子”的。而泛型编程是一种编写与具体数据类型无关的代码,以实现代码重用的编程范式。模板是泛型编程的基础

2、函数模板

2.1、模板使用

        我先写一个函数模板的例子,一会再告诉大家是一个什么样子的格式:

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

	double c = 1.5, d = 2.0;
	cout << Add(c, d) << endl;
}

运行结果: 

        上面我们就实现了一个模板,让我们编译器自己生成这些函数。

2.2、模板样式:

函数模板样式:

template<typename T1,typename T2...>

在函数中我们用T1,T2......代替变量和返回值类型。

typename是用来定义模板参数关键字,也可以使用class。但是切记不能用struct。

        其实模板就像是一个蓝图一样,告诉编译器这个函数是什么样子的,然后再编译器编译阶段,我们的编译器再去推导出这个函数。

2.3、函数模板实例化

        实例化分为显示实例化和隐式实例化。我们先来说隐式实例化。

2.3.1、隐式实例化

        上面那串代码我们就是使用的隐式实例化,其实就是让编译器自己去推导怎么生成这个函数。

那么这时候就可能会有问题出现,如果我们看下面这样一个场景:

        简单来讲,如果我们的模板和传入的参数发生了类型冲突,就会报错。在不改变模板的前提下这种情况有两种解决方式,第一,用户手动去强制转换掉其中一个参数,让模板和类型匹配上。第二,使用显式实例化。

2.3.2、显式实例化

        显式实例化就是告诉编译器我们的模板参数关键字到底是什么。

  我们对上面的代码进行更改:

#include<iostream>
using namespace std;
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<int>(a1, d1);
	Add<double>(d1, a2);

}

     尖括号里面的内容就是我们的T应该是的类型。这时候编译器就会强制转换不对的类型,让他和你给定的类型匹配上。

我们也可以这样,同时给两个类型:

#include<iostream>
using namespace std;
template<class T,class T1>
T Add(const T& left, const T1& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add<int,double>(a1, d1);
	Add<double,int>(d1, a2);

}

 2.4、模板参数匹配规则

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

        其实就是说,我们可以写成这样:

#include<iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int Add(int& left, int& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	Add(a1, a2);
}

        那么其实这样的话对于调用Add函数有很多种情况。我在这直接给出结论:优先更匹配,其次现成的。

        什么意思?如果现在我们进行传参,他和现有函数完全匹配,那就直接调用这个现有函数;如果和现有函数不匹配,但是模板能够生成更匹配的,那就调用模板;如果又和现成的不匹配,也没办法生成更匹配的函数,那就用现成的函数。

3、类模板

        在接触过函数模板之后类模板也就很容易理解了。

        类模板的定义格式:

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

         类模板举例:

#include<iostream>
using namespace std;
template<typename T>
class Stack
{
public:
	Stack(size_t capacity = 4)
	{
		_array = new T[capacity];
		_capacity = capacity;
		_size = 0;
	}
	void Push(const T& data);
private:
	T* _array;
	size_t _capacity;
	size_t _size;
};

//类模板中函数的定义
template<typename T>//注意这一行不能少
void Stack<T>::Push(const T& data)
{
	_array[_size] = data;
	++_size;

}
int main()
{
	Stack<int> A;
	Stack<double> B;
}

        需要注意的是,Stack是类名,而Stack<int>才是类型名。

        类模板的声明和定义不能文件分离,否则会报错。

        好了,今天的内容就分享到这,我们下期再见!

 

 

Logo

2万人民币佣金等你来拿,中德社区发起者X.Lab,联合德国优秀企业对接开发项目,领取项目得佣金!!!

更多推荐