C++笔记4.86

知识点1 继承中的静态成员特性(了解)

静态成员属于类而不属于对象成员,需要在类内声明类外定义(复习)

只要在子类和父类之间产生了同名,那么优先选择子类的成员函数,要想使用父类的必须添加作用域。

知识点2 多继承(了解)

我们可以从一个类继承,我们也可能同时从多个类继承,这就是多继承。但多继承是非常受到争议的,多个类的继承可能导致函数、变量等同名导致二义性。

多继承的格式

  1. class 子类: 继承方式1 父类名1,继承方式2 父类名2, 继承方式3 父类名3  
  2. {  
  3.       
  4. };  

多继承的二义性:从base1中继承一个a,从base2中也继承一个a,此时编译器不知道在子类中要使用哪一个a,必须加作用域以区分同名成员。

多继承容易产生菱形继承:两个派生类继承同一个基类,而又有一个类同时继承了这两个类,这种继承被称为菱形继承或者钻石继承。

例如:动物这个基类派生出羊和驼两个派生类,同时一个羊驼类继承了羊和驼这两个类,当羊驼调用函数或者数据的时候,就会产生二义性。

羊驼继承自动物的函数和数据继承了两份,其实只需要一份数据就可以。

对于解决调用二义性问题,可以通过指定调用哪个基类的方式来解决。但是重复继承问题无法这样解决。为此,C++引入虚基类!

知识点3 菱形继承具有公共祖先

在vs开发者模式中可以查看详细的类构造。

知识点4 虚继承(了解)

方法:在继承时,在继承方式前加上virtual修饰

继承的动作称为虚继承,父类称为虚基类

  1. class 子类:virtual public 父类;  
  2. {  
  3.       
  4. };  

使用虚继承时,子类中会自动生成vbptr(虚基类指针)virtual base pointer

vbtable(虚基类表)

vbptr指向虚基类表,虚基类表中存放了当前虚指针相对于虚基类的首地址的偏移量

之所以产生vbptr和vbtable的目的就是为了保证不管多少的继承,虚基类的数据只有一份

通过内存图我们可以发现普通继承和虚继承的内存图是不一样的。我们也可以猜到编译器肯定做了一部分修改。使得这种菱形继承在继承时只能继承一份数据,并且解决了二义性的问题。当使用虚继承时,虚基类是被共享的,也就是在继承体系中无论被继承多少次,对象内存中均只会出现一个虚基类的子对象。

注意:虚继承只能解决具备公共祖先的多继承所带来的二义性问题,不能解决没有公共祖先的多继承的问题。在工程开发中真正意义上的多继承几乎不被使用,因为多重继承带来的代码复杂性远远多于其带来的便利性,多重继承对代码维护性的影响是灾难性的,在设计方法上,任何多重继承都可以用单继承代替。

知识点5 多态的概述

多态的概念

多态是C++的特征之一,多态的分类分为静态多态(静态联编)、动态多态(动态联编)

静态多态(静态联编):函数的入口地址是在编译阶段确定(运算符重载、函数重载)

动态多态(动态联编):函数的入口地址是在运行阶段确定(虚函数)

引入

C++的动态多态主要是通过虚函数实现的

  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. class Animal  
  6. {  
  7. public:  
  8.     void sleep(void)  
  9.     {  
  10.         cout << "animal 动物在睡觉" << endl;  
  11.     }  
  12. };  
  13.   
  14. class Cat:public Animal  
  15. {  
  16. public:  
  17.     void sleep(void)  
  18.     {  
  19.         cout << "cat 猫在睡觉" << endl;  
  20.     }  
  21. };  
  22.   
  23. int main()  
  24. {  
  25.     //用基类(指针或引用)保存子类对象(向上转换)  
  26.     Animal *p = new Cat;  
  27.     p->sleep();//调用的是基类的sleep  
  28.     //需求:用基类指针保存子类对象,同时还要操作子类成员  
  29. }  

总结:基类指针只能访问子类对象中的基类部分数据

使用基类指针、引用访问子类对象中的成员方法(虚函数)

适应virtual修饰成员函数,该函数就是虚函数

  1. class Animal  
  2. {  
  3. public:  
  4.     virtual void sleep(void)  
  5.     {  
  6.         cout << "animal 动物在睡觉" << endl;  
  7.     }  
  8. };  

vfptr虚函数指针指向的是虚函数表(vftable),vftable存放vfptr所保存的函数入口地址。所以,虚函数的本质是一个函数指针变量

如果animal没有涉及到继承,函数指针变量就指向自身的sleep!

拥有虚函数的类涉及到继承

  1. class Cat:public Animal  
  2. {  
  3. public:  
  4.     virtual void sleep(void)  
  5.     {  
  6.         cout << "cat 猫在睡觉" << endl;  
  7.     }  
  8. };  

通过基类的虚函数指针查看的虚函数表中保存的函数地址被更新成了子类的函数!

总结:

当虚函数涉及继承的时候,子类会继承父类的(虚函数指针vfptr 虚函数表vftable),编译器会将虚函数表中的函数入口地址更新成子类的同名(返回值,参数相同)的函数入口地址。

如果用基类指针、引用访问虚函数的时候就会间接调用子类的虚函数。

虚函数的应用案例(基类指针、引用作为函数的参数)

  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. class Base  
  6. {  
  7. public:  
  8.     virtual void sleep(void)  
  9.     {  
  10.         cout << “父亲在睡觉” << endl;  
  11.     }  
  12. };  
  13.   
  14. class Son1:public Base  
  15. {  
  16. public:  
  17.     virtual void sleep(void)  
  18.     {  
  19.         cout << “son1 在睡觉” << endl;  
  20.     }  
  21. };  
  22.   
  23. class Son2:public Base  
  24. {  
  25. public:  
  26.     virtual void sleep(void)  
  27.     {  
  28.         cout << “son2 在打呼噜” << endl;  
  29.     }  
  30. };  
  31.   
  32. class Son3:public Base  
  33. {  
  34. public:  
  35.     virtual void sleep(void)  
  36.     {  
  37.         cout << “son3 在假装睡觉” << endl;  
  38.     }  
  39. };  
  40.   
  41. //以基类指针作为函数的参数,函数可以操作该基类派生出的任意子类对象  
  42. void sleepFun(Base &ob)  
  43. {  
  44.     ob.sleep();  
  45. }  
  46.   
  47. int main()  
  48. {  
  49.     Son1 ob1;  
  50.     Son2 ob2;  
  51.     Son3 ob3;  
  52.     sleepFun(ob1);  
  53.     sleepFun(ob2);  
  54.     sleepFun(ob3);  
  55.     return 0;  
  56. }  

C++如何实现动态绑定

先了解编译器如何处理虚函数。当编译器发现类中有虚函数的时候,编译器会创建一张虚函数表,把虚函数的函数入口地址放到虚函数表中,并且在类中秘密增加一个指针,这个指针vptr就是指向对象的虚函数表。在多态调用的时候,根据vptr指针,找到虚函数表来实现动态绑定。

“C++笔记4.86”的3个回复

  1. Thanks for another fantastic article. Where else could anybody get that
    kind of information in such a perfect method of writing?

    I have a presentation subsequent week, and I’m at the look for such information.

  2. Hello there, just became alert to your blog through Google, and found that it is really informative.

    I’m going to watch out for brussels. I’ll be grateful if you continue
    this in future. Lots of people will be benefited from your writing.
    Cheers!

  3. It’s perfect time to make some plans for the future and it is time to be happy.
    I’ve read this post and if I could I desire to suggest you few interesting things or tips.
    Perhaps you can write next articles referring to this article.
    I want to read more things about it!

发表评论

邮箱地址不会被公开。 必填项已用*标注