• (C/C++学习)5.C++中的虚继承-虚函数-多态解析


    说明:在C++学习的过程中,虚继承-虚函数经常是初学者容易产生误解的两个概念,它们与C++中多态形成的关系,也是很多初学者经常产生困惑的地方,这篇文章将依次分别对三者进行解析,并讲述其之间的联系与不同。

    一.虚继承

    1.在多继承中,对于多个父类的数据及函数成员,虽然有时候把他们全部继承下来是有必要的,比如当这些成员都不同的时候。但在大多数的情况下,比如当多个父类之中的成员有重叠的地方时,因为保留多份数据成员的拷贝,不仅占有了较多的存储空间,还增加了访问的难度(由于继承了来自多个父类的同名数据成员,访问时需要加上父类的作用域,比如“父类名::成员”),因此,在实际的继承中是没必要的。而虚继承则可以完美的解决这一问题。

    2.在虚继承中,被虚继承的类叫做虚基类,虚基类是需要设计和抽象的,它应当提取多继承父类中重叠的部分作为成员,虚继承是对继承的一种扩展。

    示例1:

      1 #include<iostream>
      2 using namespace std;
      3 
      4 class furniture
      5 {
      6 public:
      7     furniture(float l,float wi,float we)
      8         :len(l),wid(wi),weight(we)
      9     {}
     10     void dis()
     11     {
     12         cout<<"len = "<<len<<endl;
     13         cout<<"wid = "<<wid<<endl;
     14         cout<<"weight="<<weight<<endl;
     15     }
     16 protected:
     17     float len;
     18     float wid;
     19     float weight;
     20 };
     21 
     22 //+++++++++++++++++++++++++
     23 
     24 class bed:virtual public furniture
     25 {
     26 public:
     27     bed(float l,float wi,float we)
     28         :furniture(l,wi,we)
     29     {}
     30 
     31     void sleep(){cout<<"go to sleep!!!!!"<<endl;}
     32 };
     33 
     34 //+++++++++++++++++++++++++
     35 
     36 class sofa:virtual public furniture
     37 {
     38 public:
     39     sofa(float l,float wi,float we)
     40         :furniture(l,wi,we)
     41     {}
     42 
     43     void sit(){cout<<"go to have a rest!!!!!"<<endl;}
     44 };
     45 
     46 //+++++++++++++++++++++++++
     47 
     48 class sofabed:public bed,public sofa
     49 {
     50 public:
     51     sofabed(float l,float wi,float we)
     52         :furniture(l,wi,we),bed(1,2,3),sofa(1,2,3)
     53     {}
     54 };
     55 
     56 int main()
     57 {
     58     bed b(1,2,3);
     59     b.sleep();
     60     b.dis();
     61     sofa s(2,3,4);
     62     s.sit();
     63     s.dis();
     64     sofabed sb(4,5,6);
     65     sb.sleep();
     66     sb.sit();
     67     sb.dis();
     68     return 0;
     69 }
     70 
    查看代码

    程序运行结果:

    1获

    在本例中,如果仅仅采用的是多继承而非虚继承,如下代码所示:

      1 #include<iostream>
      2 using namespace std;
      3 
      4 class bed
      5 {
      6 public:
      7     bed(float l,float wi,float we)
      8         :len(l),wid(wi),weight(we)
      9     {}
     10 
     11     void sleep()
     12     {
     13         cout<<"go to sleep!!!!!"<<endl;
     14     }
     15 
     16     void dis()
     17     {
     18         cout<<"len = "<<len<<endl;
     19         cout<<"wid = "<<wid<<endl;
     20         cout<<"weight = "<<weight<<endl;
     21     }
     22 protected:
     23     float len;
     24     float wid;
     25     float weight;
     26 };
     27 //+++++++++++++++++++++++++++++
     28 class sofa
     29 {
     30 public:
     31     sofa(float l,float wi,float we)
     32         :len(l),wid(wi),weight(we)
     33     {}
     34     void sit()
     35     {
     36         cout<<"go to have a rest!!!!!"<<endl;
     37     }
     38     void dis()
     39     {
     40         cout<<"len = "<<len<<endl;
     41         cout<<"wid = "<<wid<<endl;
     42         cout<<"weight = "<<weight<<endl;
     43     }
     44 protected:
     45     float len;
     46     float wid;
     47     float weight;
     48 
     49 };
     50 //+++++++++++++++++++++++++++
     51 class sofabed:public bed,public sofa
     52 {
     53 public:
     54     sofabed(float l,float wi,float we)
     55         :bed(l,wi,we),sofa(l,wi,we)
     56     {}
     57 };
     58 //+++++++++++++++++++++++++++
     59 int main()
     60 {
     61     bed b(1,2,3);
     62     b.sleep();
     63     b.dis();
     64     sofa s(2,3,4);
     65     s.sit();
     66     s.dis();
     67     sofabed sb(5,6,7);
     68     sb.sit();
     69     sb.sleep();
     70     sb.sofa::dis();
     71     sb.bed::dis();
     72     return 0;
     73 }
    查看代码

    则sb.dis()就有问题了;因为它产生了二义性,编译器不知道该调用哪一个父类的成员函数,而正确做法是加上父类的作用域,这无疑是增加了访问了难度。

    结论:多继承带来的数据存储多份,占用内存空间较多,并且访问不便(作用域的增加),采用虚继承可以解决这一问题。

    二.纯虚函数

    1.纯虚函数的格式:

      1 class A
      2 {
      3     virtual void func() = 0;
      4 }

    2.含有纯虚函数的类为抽象基类,不可创建对象,其存在的意义就是被继承,提供族类的公共接口,

    3.纯虚函数只有声明,没有实现,被初始化为0,

    4.如果一个类中声明了纯虚函数,而在派生类中没有对该函数定义,则该函数在派生类中仍然为纯虚函数,派生类仍然为纯虚基类,

    5.含有虚函数的类,析构函数也应该声明为虚函数,这样在delete父类指针的时候,才会调用子类的析构函数,实现完整析构,

      1 #include<iostream>
      2 using namespace std;
      3 
      4 class A
      5 {
      6 public:
      7     A()
      8     {
      9         cout<<"A(){}"<<endl;
     10     }
     11     virtual ~A()
     12     {
     13         cout<<"~A(){}"<<endl;
     14     }
     15     virtual void func() = 0;
     16 };
     17 class B:public A
     18 {
     19 public:
     20     B(){cout<<"B(){}"<<endl;}
     21     ~B(){cout<<"~B(){}"<<endl;}
     22     virtual void func()
     23     {
     24         cout<<"B.func(){}"<<endl;
     25     }
     26 };
     27 int main()
     28 {
     29     A*pa = new B;
     30     pa->func();
     31     delete pa;
     32     return 0;
     33 }
     34 
    查看代码

    程序运行结果:

    2获

    注意:若在此例中,没有将含有虚函数的父类析构函数声明为虚函数,则将不会调用子类的析构函数~B()实现完整析构。

    三.多态的实现

    1.C++中的多态指的是由于继承而产生的相关的不同的类,其对象对同一消息会做出不同的反应。

    2.多态实现的前提是赋值兼容,赋值兼容的内容如下:

        a.子类的对象可以赋值给基类的对象,

        b.子类的对象可以赋值给基类的引用,

        c.子类对象的地址可以赋值给基类的指针(一般用于动多态的实现),

        d.在赋值后,子类对象就可以作为基类对象使用,但只能访问从基类继承的成员.

    3.动多态的实现条件:

        a.父类中有虚函数,

        b.子类override(覆写)父类中的虚函数,

        c.将子类的对象赋值给父类的指针或引用,由其调用公用接口.

      1 #include<iostream>
      2 using namespace std;
      3 
      4 class Shape
      5 {
      6 public:
      7     virtual void draw() = 0;
      8 };
      9 //+++++++++++++++++++
     10 class Circle:public Shape
     11 {
     12 public:
     13     void draw()
     14     {
     15         cout<<"Circle"<<endl;
     16     }
     17 };
     18 //+++++++++++++++++++
     19 class Rect:public Shape
     20 {
     21 public:
     22     void draw()
     23     {
     24         cout<<"Rect"<<endl;
     25     }
     26 };
     27 int main()
     28 {
     29     Circle c;
     30     Rect r;
     31     Shape *p = &c;
     32     p->draw();
     33     p = &r;
     34     p->draw();
     35     return 0;
     36 }
     37 
    查看代码

    注意:C++中的多态一般指动多态,其实C++中函数的重载也是一种多态现象,其通过命名倾轧在编译阶段决定,故称为静多态;而动多态一般是在父子类中在运行阶段决定的。

  • 相关阅读:
    前端html+css标签简介(可能就我自己看的懂-。-)
    python-day43(正式学习)
    python-day42(正式学习)
    python-day40(正式学习)
    python-day39(正式学习)
    python-day38(正式学习)
    python-day37(正式学习)
    python-day31(正式学习)
    python-day30(正式学习)
    python-day29(正式学习)
  • 原文地址:https://www.cnblogs.com/tuihou/p/9718335.html
Copyright © 2020-2023  润新知