当前位置:   article > 正文

C++中的静态和动态多态_c++静态多态和动态多态

c++静态多态和动态多态

之前学过继承,子类继承父类的属性,多态就是基于继承而来的,我们在如果只用继承,那么子类继承父类的各种属性在编译环节,就已经被确认了,导致代码不灵活。如果继承下来的某个子类不支持某种问题的解决,那么父类就需要重新编写代码,这样这个子类ok了,其他子类却有可能出现问题。

多态的好处:灵活,提高代码的可复用性。

1、多态的分类

1、静态多态:函数重载和运算符重载属于静态多态,复用函数名(编译期多态);

2、动态多态:派生类和虚函数实现运行时多态(运行期多态)。

函数重载就不必多说了,就是函数名的复用;运算符重载也类似于函数重载,相当于把符号的功能扩大了,同一个符号或者函数,能够实现多种功能,它们都是在编译的时候绑定了,以后都不能改变了,所以叫编译器多太。

2、动态多态的实现

我们以汉堡为例,汉堡为父类,鸡腿堡和牛肉保都继承了汉堡类。

代码:

  1. #include<iostream>
  2. using namespace std;
  3. class baseHamburger//父类------汉堡类
  4. {
  5. public:
  6. void Material()
  7. {
  8. cout << "汉堡的配方为:面包、酱汁、蔬菜、肉饼"<<endl ;
  9. }
  10. };
  11. class chickenHamburger:public baseHamburger//子类1------鸡腿堡类
  12. {
  13. public:
  14. void Material()
  15. {
  16. cout << "汉堡的配方为:面包、酱汁、蔬菜、鸡肉饼" << endl;
  17. }
  18. };
  19. class beefHamburger:public baseHamburger//子类2------牛肉堡类
  20. {
  21. public:
  22. void Material()
  23. {
  24. cout << "汉堡的配方为:面包、酱汁、蔬菜、牛肉饼" << endl;
  25. }
  26. };
  27. int main()
  28. {
  29. chickenHamburger p1;
  30. p1.Material();
  31. beefHamburger p2;
  32. p2.Material();
  33. }

从代码可知,我们父类和2个子类内存在3个同名的函数Material(),我们创建p1和p2,他们都是默认调用类内的Material()函数,而不是调用父类的Material()函数,在程序编译的时候就确认了Material()函数的地址。如果我们想要调用父类的Material()函数,那么只需要加作用域就行。这也是我们最常见的情况。

如果我们这样写

  1. #include<iostream>
  2. using namespace std;
  3. class baseHamburger//父类------汉堡类
  4. {
  5. public:
  6. void Material()
  7. {
  8. cout << "汉堡配方为:面包、酱汁、蔬菜、肉饼" << endl;
  9. }
  10. };
  11. class chickenHamburger:public baseHamburger//子类1------鸡腿堡类
  12. {
  13. public:
  14. void Material()
  15. {
  16. cout << "汉堡配方为:面包、酱汁、蔬菜、鸡肉饼" << endl;
  17. }
  18. };
  19. class beefHamburger :public baseHamburger//子类2------牛肉堡类
  20. {
  21. public:
  22. void Material()
  23. {
  24. cout << "汉堡配方为:面包、酱汁、蔬菜、牛肉饼" <<endl;
  25. }
  26. };
  27. //创建一个吃的函数
  28. void eat(baseHamburger & Hamburger)
  29. {
  30. cout << "我吃的";
  31. Hamburger.Material();
  32. }
  33. int main()
  34. {
  35. chickenHamburger p1;
  36. eat(p1);
  37. beefHamburger p2;
  38. eat(p2);
  39. }

 

 我们创建一个吃的eat()函数,函数参数为父类对象,函数体为父类对象指向和子类同名的函数,

此时,编译器默认调用了父类的Material()函数,为什么呢?

1、我们明明创建的是子类对象,那应该调用的是子类的Material()呀!

2、传入eat()函数的参数是父类对象,那应该就是调用父类的Material()呀!

是不是感觉有歧义?

那怎么解决呢?

只需要在父类的Material()函数前加上virtual关键字就可以解决问题。

  1. class baseHamburger//父类------汉堡类
  2. {
  3. public:
  4. virtual void Material()//虚函数
  5. {
  6. cout << "汉堡配方为:面包、酱汁、蔬菜、肉饼" << endl;
  7. }
  8. };

加上virtual关键字后函数就变成了虚函数,程序运行的时候才确定Material()函数的地址。

有的同学可以说,我直接创建几个不同的函数名不就行了?

这样确实可以,但是你别忘了这个问题是在同名函数,我们要复用函数名前提下提出来的。

而且就算你创建很多个不同的函数名,那代码量不就上来了?且每一个eat()函数的参数都要额外写一个子类对象,函数体实现也就变得复杂了。

我们的目的是传入什么对象,那么就调用什么对象的函数,这也是动态多态的好处。

3、 动态多态的满足条件

1、有继承关系;
2、子类重写父类中的虚函数。

动态多态使用:
父类指针或引用指向子类对象,就像我们的eat()函数一样。

 4、多态内部原理

当我们继承了父类中的函数的时候,那仅仅是继承了这个函数,其内部是通过虚函数表指针(vfptr)的和虚函数表(vftable)来控制的,vfptr又指向vftablevftable内存放了所有父类的函数地址,vfptr就指向这些地址,所以子类的内部就固定存在父类的一份vfptrvftable拷贝,这是最一般的情况。

但是当我们将父类的函数改为虚函数的时候,且在子类重写父类的虚函数,那就不一样了,当我们用父类指针指向子类对象的时候,vftable中同名函数的作用域就从父类变成的子类,相当于把父同名类函数的地址改为子类同名函数的地址。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/427679
推荐阅读
相关标签
  

闽ICP备14008679号