当前位置:   article > 正文

04.17_111期_C++_继承&多态_虚表

04.17_111期_C++_继承&多态_虚表

 如果想实现不同的类的对象调用不同的虚函数
 可以通过   协变   这种形式
 在父类Person中的虚函数的返回值 的数据类型是一个  父类(任意一个父类σ就行)
 在子类Student中的虚函数的返回值 的数据类型是一个  继承了σ的子类

  1. class Student : public Person
  2. {
  3. public:
  4. virtual B* BuyTicket()
  5. {
  6. cout << "买票-半价" << endl;
  7. return nullptr;
  8. }
  9. //如果不写 virtual 下述析构函数
  10. //那么只要定义一个 非Student 类型的对象,就无法调到~Student()这个函数
  11. //导致 _ptr无法释放空间
  12. virtual ~Student()
  13. {
  14. delete[] _ptr;
  15. cout << "~Student()" << endl;
  16. }
  17. private:
  18. int* _ptr = new int[10];
  19. };
  1. //实际上 父子类 中析构函数的函数名最终会处理成一样的
  2. //那么是否应该使用virtual来修饰自定义的 父子类中的析构函数呢?
  3. int main()
  4. {
  5. Person* p1 = new Person;
  6. Person* p2 = new Student;
  7. //delete这条指令代表了 析构函数 和 operator delete(p1)
  8. //delete在调用析构函数的时候只会看两点
  9. /*1. 根据指针的类型,如果是Person类型的指针,
  10. 此时无论指向的是什么,都会调用Person这个类中的析构函数 ~Person
  11. 2. 根据父子类中有没有对应的 自定义 虚 的析构函数
  12. 如果有,且构成多态,那么就是指向谁,析构谁,
  13. 比如上述的代码中p2指向的是一个Student对象,
  14. 那么此时delete p2,将会调用Student的析构函数 virtual ~Student()
  15. 注意!!!!!!!!!!!!!!!!!!!!!!!!
  16. 一、如果只通过 1 来调用析构函数,那么可能会造成无法调到指定类型的析构函数问题
  17. 这是因为指针的类型不一定和 所指向的 数据类型相同
  18. 根据指针类型 来调用 析构函数
  19. 那么只能通过 在父子类中对析构函数分别加上 virtual
  20. 二、上述一、中给父子类的析构函数加上 virtual就能构成多态的原因是:
  21. 编译器会把所有析构函数都处理成同名同参的函数,都改成destructor()的函数
  22. 以方便这两个函数构成重写*/
  23. delete p1;
  24. delete p2;
  25. return 0;
  26. }

综上,虚函数virtual的使用有三个例外
      1. 协变:父子类中不完全三同, 返回值类型 是父子类
      2. 父子类中析构函数都加上virtual
    3. 子类中的虚函数可以不加virtual

例题1:
对于下面的程序,其执行结果是 B->1,需要注意的是
      1. 首先有一个指p 指向了 一个B 类对象,
         对于p->test(); 值得说明的是:
         1.1 由于 p 是一个 B类型的指针,所以 会在 B 类型中寻找一个名叫test()的成员函数
         1.2 B类继承了A,要找到test(),也可以从 A 类中寻找
      2. A类中的test()要调用 func(),那么需要问,构不构成多态调用?
         构成,这是因为:
         2.1 父类和子类中都有func,且返回值,且参数类型相同,且父类中的func用virtual修饰
         2.2 调用func的时候是通过 A类中this指针进行调用的,
             值得注意的是:
             this指针的数据类型是 A*,这是因为test()是属于A这个类域的
         所以构成多态调用
      3. 基于2.,由于A::this->func();构成多态调用
         所以会根据 指针指向的对象来确定调用哪个函数,
         注意:和什么类型的指针没有关系,只取决于被指向的数据类型,
         也就是只取决于一开始在调用test()这个函数时,
         写下σ->test()的这个σ究竟是什么类型,对于下面的程序
         σ的类型是有new B 来决定的,σ的类型是B
         所以应该调用B中的func函数
    4. 虽然会执行 cout << "B->" << val << endl; 的这个定义
         但是函数的声明是  virtual void func(int val = 1) 
         这就是多态调用的特点,需要按照3. 先找到σ的类型,把这个类型中的func找到后
         把其中的实现代码  重写  到 2.中用 this 指针进行调用的 func() 函数中
         也就是 (根据2.) 使用 A 中func的声明,(根据3.) 使用 B 中 func的定义

例题2:
 多继承中由于public Base1, public Base2 表明
 Base1比Base2先继承,那么Base1 的会先开空间
 所以Base1先开空间,Base2后开空间
 Derive会指向包含父类空间和子类所需空间的一开始,
 所以 p3 指针变量的值和 p1 指针变量的值相同,且小于p2

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

闽ICP备14008679号