100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > C++ 泛型编程(一):模板基础:函数模板 类模板 模板原理 模板匹配规则

C++ 泛型编程(一):模板基础:函数模板 类模板 模板原理 模板匹配规则

时间:2019-09-24 23:10:10

相关推荐

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);/*这时编译器就会报错,因为需要通过参数推演出对应类型,但是此时就无法推演出到底是将T推演成double还是将T推演成intAdd(i, a);*/// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化Add(i, (int)a);return 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才是类名

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