虚函数和模板的用途区别

NeoZy 2020-03-04 02:15:00
原文地址:https://www.cnblogs.com/FdWzy/p/12406183.html

模板是范型编程的基础,所谓范型编程就是用独立与任何特定类型的方式编写代码
所以简单地说,类是对象的抽象,而模板又是类的抽象,也就用模板能定义出具体类

再理解深刻点
在c++里,常说的多态一般分为两种:
一种是运行时的多态,也就是虚函数体现的多态
另一种是编译时的多态,也就是范型编程的多态,体现在参数的多态

在作用上是为了提高编程效率,其实用其他技术也能达到同样的效果

另外贴一篇讲虚函数的存在意义的知乎回答,写的实在太清楚了:

作者:乌索普
链接:https://www.zhihu.com/question/23971699/answer/84332610
虚函数主要是用来实现多态和多重继承的
没有虚函数理论上也可以实现多态,但是太麻烦了,没有虚函数清晰
主要是在多态上使用。

多态是什么呢:
多态就是一个指针指向子类对象,那么他调用的函数就是子类的对象的。
这就实现了什么?
实现了一个函数会根据传入参数的不同有不同的功能。
函数有多个状态。 就是多态
> void fuck (Animal *a);

如果没有多态 那么 这个函数 只能fuck Animal
想要fuck多种 就要用重载,写多个同名函数 参数不同
但是有了多态 他可以fuck所有Animal的子类 比如牛 羊 耗子 什么的
根据传进来的参数不同,fuck不同的动物。
这样这个函数就是fuckAnimalYouWant 函数了
这就多态了
如果现在你想fuck狗
要做的只是:
1.创建狗类继承Animal
2.传入到fuck函数中
此时 并不需要该代码 即使传入到fuck函数中 也可以是通过配置文件这种形式改变传入参数,并没有改变代码

如果没有多态呢?
1.创建狗对象继承Animal
2.在类中增加一个fuck函数 参数是Dog

这就改变代码了。
违反了设计模式开闭原则:对扩展开放,对修改关闭

实现多态的方式是什么
子类有和父类 同名同参数(重写) 的函数
所以 对于多态的状态来说,一定会有两个同名同参数函数,分别定义在子类中和父类中

那么当用指针调用一个函数的时候,究竟是调用子类的还是父类的:

对于一个类成员函数的调用:
Animal *a = new Dog()
a->bark()
表面上看来是在一个对象内部去调用函数,好像是这个函数是存在于对象内部的感觉。。
但是实际上不是
实际上所有的函数都存放在单独的一个代码区,而函数对象里面只有成员变量。
也就是说
a->bark()
实际上被编译器变成了
Animal::bark(a) 把对象a传进去 才告诉了编译器具体是哪个对象调用的这个函数
但是对于多态来说:
Animal::bark() 有
Dog::bark()也一定有
究竟选哪个函数执行呢?

此时还没运行,还是在编译阶段,也就是内存中什么Dog啊 Animal 的对象还没有存在。
所以只能根据a这个指针的类型 来决定使用哪个。也就是 Animal::bark()
所以
无论a指向什么子类,他调用的bark函数 都是Animal版本的父类版本的bark函数。

所以
现在没有办法达到多态的效果

这个在编译时决定函数是哪个类的函数的方式就叫做静态联编

a->bark()
现在希望调用的是Dog::bark(a)
调用的是Dog类的
所以就不能使用静态联编,静态联编在编译的时候决定函数是哪个。
为什么知道调用的是Dog::bark(a) 而不是 Cat::bark(a)呢?
因为a指向的对象是一个Dog 而不是一个Cat
所以必须在a指向的对象创建出来后才能决定究竟调用哪个函数
这就是动态联编

怎么把静态联编改成动态联编呢??
编译器在静态联编时做了以下转换
a->bark ---> Animal::bark(a)
当bark函数被设置为虚函数时,就不会进行那个转换了,而是转化为
a->bark ----》 (a->vtpl[1])(a)

先通过a指针找到对象,再找到对象中的虚表,再在虚表里面找到该调用的那个函数的函数指针

因为必须要在a指向的对象里面找,所以 必须等到a被创建出来,所以必须是运行时
所以这就是动态联编 运行时决定调用哪个函数

而这个vtpl就是虚表指针,
当类中出现一个virtual指针时编译器就自动在类的成员变量里加入一个指针成员。
这个指针成员指向这个类的虚表指针。

当子类重写父类方法时 同时也是把继承自父类的虚表中对应函数的索引的函数指针从父类函数改成了自己的函数。。

这就造成了子类和父类对象里面的虚表内容不同。
所以动态联编时 去子类 或者父类里面找虚表,
调用的函数不同。

就形成了多态的效果

这个过程其实自己写逻辑也能写出来。
但是官方作出了virtual就是为了释放这个工作量
当你写了virtual这个关键字 ,以上的创建虚表 继承 等一系列工作就都自动执行了。

实现了面向对象的思想
但是降低了执行效率。

声明:该文章系转载,转载该文章的目的在于更广泛的传递信息,并不代表本网站赞同其观点,文章内容仅供参考。

本站是一个个人学习和交流平台,网站上部分文章为网站管理员和网友从相关媒体转载而来,并不用于任何商业目的,内容为作者个人观点, 并不代表本网站赞同其观点和对其真实性负责。

我们已经尽可能的对作者和来源进行了通告,但是可能由于能力有限或疏忽,导致作者和来源有误,亦可能您并不期望您的作品在我们的网站上发布。我们为这些问题向您致歉,如果您在我站上发现此类问题,请及时联系我们,我们将根据您的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。