1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > C++ 泛型编程(一):模板基础:函数模板 类模板 模板推演成函数的机制 模板实例

C++ 泛型编程(一):模板基础:函数模板 类模板 模板推演成函数的机制 模板实例

时间:2020-06-24 04:20:03

相关推荐

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;}

就比如非常常用的swap函数,虽然c++支持重载,但是如果对于这种函数我们每一个都要自己去实现,是一件很麻烦并且多余的工作,因为我们将重复逻辑的东西多次实现。

比如我们想向一个封装好的类添加新的数据成员,重新写一个不如一开始就写成泛型。

class Stack{private:int capacity;int size;int* arr;};

如果我们实现了一个栈,如果他此时要存储其他的数据类型,那我们就必须还要重新修改一个,这无疑是繁琐的。

所以,C++基于重载这一项机制,实现了模板编程,模板作为类或者函数的蓝图,可以针对不同类型,来实现对应操作。

函数模板

函数模板与类型无关,被调用时根据实参类型产生函数的对应类型版本。

template<typename T1, typename T2,......,typename Tn>//typename即类型名,也可以用class替代,这里的class指的是类型,不能用struct替换

编写函数模板时,只需要在对应的函数前面加上上面的语句即可,然后将所有需要替换类型的参数改为上面的T1、T2即可

template<classT>void Swap(T& left, T& right) {T temp = left;left = right;right = temp;}int main(){int i = 3, j = 4;double a = 3.4, b = 5.6;char x = 'x', y = 'y';Swap(i, j);Swap(a, b);Swap(x, y);cout << i << ' ' << j << endl;cout << a << ' ' << b << endl;cout << x << ' ' << y << endl;}

当我们调用这个模板函数的时候,是生成一个函数重载并调用三次呢?还是生成三个返回值不同的函数各调用一次呢?

通过地址信息可以看到,它调用的是三个不同的函数。

因此,模板函数本身并不是一个函数,而是一个蓝图,通过识别我们传入的参数,然后在底层生成一个该类型的模板函数,并调用该函数。

并且这个阶段是在预处理的时候就进行了,因为如果它是在编译阶段才进行函数的生成,那肯定是无法通过语法的检查的,所以这一阶段只能在预处理进行。

函数模板实例化

函数模板只在第一次使用的时候才会实例化出来,它的实例化有两种方法,一种是显式实例化,一种是隐式实例化。

隐式实例化

template<class T> T Add(const T& left, const T& right) {return left + right;}int main(){int i = 3, j = 4;double a = 3.4, b = 5.6;Add(i, j);Add(a, b);/*Add(i, a);这时编译器就会报错,因为需要通过参数推演出对应类型,但是此时就无法推演出到底是将 T 推演成 double 还是将 T 推演成 int*/// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化Add(i, (int)a); // 强制转化,将 T 推演成 intreturn 0;}

显式实例化

int main() {int i = 3;double x = 3.4;// 显式实例化Add<int>(i, x);return 0; }

用尖括号来显式的声明,STL中的容器等就是采用这种显式的实例化来明确参数的类型。

函数模板的匹配规则

一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。这时如果使用隐式实例化,则会调用非模板函数;如果使用显式实例化,则调用模板函数。

//非模板函数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); // 调用非模板函数Add<int>(1, 2); // 调用模板函数}

对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板函数。

// 专门处理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函数}

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

类模板

类模板的用法和函数模板其实一样

template<class T>class Stack{private:int capacity;int size;T* arr;};

这里的Stack不是一个具体的类,他是类模板,也就是一个蓝图,通过这个模板来识别参数的类型并生成对应的模板类。

所以可以理解为类模板是一个类家族,模板类是通过类模板实例化的具体类。

如果类中的成员函数需要在类外定义的话,需要每一个定义前都要声明一次类模板的参数列表

template<class T>class Stack{public:void push();void pop();private:int capacity;int size;T* arr;};template<class T>void Stack<T>::push(){}template<class T>void Stack<T>::pop(){}

还有一个需要注意的地方就是,类模板的所有成员函数都是模板函数

类模板的实例化

类模板只能够通过显式实例化

STL中的几个容器都是通过类模板实现的:

stack<int> s1;stack<double> s2;

stack<int>并不是类名,而是显式实例化指定的类型,stack才是类名。

C++ 泛型编程(一):模板基础:函数模板 类模板 模板推演成函数的机制 模板实例化 模板匹配规则

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。