• 第53课 被遗弃的多重继承(上)


    问题:

      C++中是否允许一个类继承自多个父类呢?答案是肯定的,这种现象就是多重继承。

      多重继承是C++中一个特有的特性,因为在其他的程序设计语言里面,如C#、java等语言只支持单重继承

    • C++支持编写多重继承的代码
      • 一个子类可以拥有多个父类
      • 子类拥有所有父类成员变量 
      • 子类继承所有父类的成员函数
      • 子类对象可以当作任意父类对象使用---赋值兼容也适用于此

    多重继承的语法规则:

        

                 多重继承的本质与单继承相同!!!

    实验:多重继承问题一

      1 #include<iostream>
      2 #include<string>
      3 
      4 //多重继承带来的问题:(1)多重继承得到的对象可以有“不同的地址”
      5 //                 (2)多重继承可能产生多余的成员
      6 
      7 using namespace std;
      8 
      9 class BaseA
     10 {
     11     int ma;
     12 
     13 public:
     14     BaseA(int a) //构造函数
     15     {
     16         ma = a;
     17     }
     18     int getA()  //成员函数
     19     {
     20         return ma;
     21     }
     22 };
     23 
     24 class BaseB
     25 {
     26     int mb;
     27 
     28 public:
     29     BaseB(int b)
     30     {
     31         mb = b;
     32     }
     33     int getB()
     34     {
     35         return mb;
     36     }
     37 };
     38 
     39 class Derived : public BaseA, public BaseB
     40 {
     41     int mc;
     42 
     43 public:
     44 
     45     //单继承------子类的构造函数------初始化列表构造父类的构造函数
     46 
     47     Derived(int a, int b, int c) : BaseA(a), BaseB(b)   //多继承--子类的构造函数--初始化列表同时调用两个父类的构造函数
     48     {
     49         mc = c;
     50     }
     51 
     52     int getC()
     53     {
     54         return mc;
     55     }
     56 
     57     void print()
     58     {
     59         //cout << "ma=" << ma << ","     //子类无法直接访问父类的private私有成员-----通过调用父类成员函数(公有的)访问私有成员变量
     60         //       << "mb=" << mb <<","
     61         //     << "mc=" << mc <<endl;
     62 
     63 
     64          //调用父类成员函数--------访问成员变量
     65         cout << "ma=" << getA() << ","  
     66               << "mb=" << getB() << ","
     67               << "mc" << mc << endl;
     68     }
     69 };
     70 
     71 int main()
     72 {
     73     cout << "sizeof(Derived)= " << sizeof(Derived) << endl;  // 3*4=12  子类内存空间大小:父类与子类数据成员的叠加ma mb mc
     74                                                              //增加子类成员函数后,内存空间大小还是12
     75                                                              //类里面成员函数和成员变量是分开存放的,
     76                                                              //每个类有自己的成员变量,都有类对象共享类的成员函数
     77     
     78     //继承构造函数调用顺序:先父母(BaseA(a), BaseB(b) ),再客人(成员变量是其他类的对象--调用成员对象的构造函数),最后自己mc = c;
     79     Derived d(1, 2, 3);       
     80 
     81     d.print();  //ma =1 ,mb =2 ,mc=3
     82 
     83     //继承两个父类成员函数
     84     cout << "d.getA()=" << d.getA() << endl;  //d.getA()=1
     85     cout << "d.getB()=" << d.getB() << endl;  //d.getB()=2
     86     cout << "d.getC()=" << d.getC() << endl;  //d.getC()=3
     87 
     88     cout << endl;
     89 
     90     //赋值兼容---父类指针pa pb同时指向子类对象d(合法)
     91     BaseA* pa = &d;   
     92     BaseB* pb = &d;
     93 
     94     cout << "pa->getA()=" << pa->getA() << endl;    //pa->getA()=1
     95     cout << "pb->getB()=" << pb->getB() << endl;    //pb->getB()=2
     96     cout << endl;
     97 
     98 
     99     //新定义指针paa pbb同时指向相同对象d
    100     void* paa = pa;  
    101     void* pbb = pb;
    102 
    103     if (paa == pbb)       //指针保存的地址值,
    104     {
    105         cout << "pointer to the same object! " << endl;
    106     }
    107     else
    108     {
    109         cout << "error" << endl;   //error
    110     }
    111          
    112     cout << "pa= " << pa << endl;     //pa=0xbfe7e304
    113     cout << "pb= " << pb << endl;     //pb=0xbfe7e308
    114     cout << "paa= " << paa << endl;     //paa=0xbfe7e304
    115     cout << "pbb= " << pbb << endl;     //pbb=0xbfe7e308
    116 
    117     //父类指针指向子类变量  pa,pb保存的地址值为什么不同??????整个期间也没有改变指针值??
    118     //答:多重继承得到的对象可以有“不同的地址”
    119 
    120 
    121     return 0;
    122 }

    分析多重继承:

    多重继承的问题一:

                     

    pa、pb指针还是指向一个对象,只是指向的位置不同----pa指向头部,pb可能指向尾部------不方便程序开发

    实际开发中,判断两个指针是不是指向同一个对象,是根据比较两个指针保存的地址值判断。

    现在是两个指针保持的地址值不同,但是确实是指向同一个对象--------没有方案处理这个问题

     多重继承的问题二:

                      

    现实生活中一个人可能扮演两个角色-------(老师,学生)

    一个Doctor继承Teacher和Student (Teacher和Student分别拥有m_name、m_age),那么此时出现Doctor有两个m_name、m_age---多余成员产生

    实验:代码描述上面类图

     1 #include<iostream>
     2 #include<string>
     3 
     4 using namespace std;
     5 
     6 class people 
     7 {
     8     string m_name;
     9     int m_age;
    10 public:
    11     people(string name, int age) 
    12     {
    13         m_name = name;
    14         m_age = age;
    15     }
    16     void print()
    17     {
    18         cout << "name= " << m_name << ","
    19              << "age= " <<  m_age  << endl;
    20     }
    21 };
    22 
    23 class teacher : public people
    24 {
    25 public:
    26     teacher(string name, int age) : people(name, age)  //调用父类构造函数
    27     {
    28     }
    29 };
    30 
    31 class student : public people           
    32 {
    33 public:
    34     student(string name, int age) : people(name, age)
    35     {
    36     }
    37 };
    38 
    39 
    40 //多重继承---------带来的数据冗余
    41 class doctor : public student, public teacher
    42 {
    43 public:
    44     //doctor(string name, int age) : student(name, age), teacher(name, age)
    45     doctor(string name, int age) : student(name+"1", age+1), teacher(name+"2", age+2)
    46     {
    47     }
    48 };
    49 
    50 int main()
    51 {
    52     doctor d("mimi", 26);
    53 
    54     //d.print();      //由于doctor继承了两个类student和teacher,所以对应doctor有两个print函数,编译器不知道调用哪个
    55                       //使用类作用域分辨符调用print();手动调用
    56     
    57 
    58     //多重继承---------带来的数据冗余
    59     d.teacher::print();   // name =mimi,age =26
    60     d.student::print();   // name =mimi,age =26
    61 
    62     //修改后
    63     d.teacher::print();   // name =mimi1,age =27
    64     d.student::print();   // name =mimi1,age =27
    65 
    66     return 0;
    67 } 

     中间父类Virtual关键字:

    • 虚继承能够解决数据冗余问题
    • 中间层父类不再关心顶层父类的初始化------顶层父类的初始化由子类完成
    • 最终子类必须直接调用顶层父类的构造函数

     虚继承发生----Teacher和Student都不再调用父类People的构造函数,而是让Doctor调用

    程序说明:

     1 #include<iostream>
     2 #include<string>
     3 
     4 //解决多重继承带来的数据冗余--------->>虚继承--------中间父类加virtual关键字
     5 using namespace std;
     6 
     7 class people
     8 {
     9     string m_name;
    10     int m_age;
    11 public:
    12     people(string name, int age)
    13     {
    14         m_name = name;
    15         m_age = age;
    16     }
    17     void print()
    18     {
    19         cout << "name= " << m_name << ","
    20              << "age= "  << m_age  << endl;
    21     }
    22 };
    23 
    24 class teacher : virtual public people
    25 {
    26 public:
    27     teacher(string name, int age) : people(name, age)  //调用父类构造函数
    28     {
    29     }
    30 };
    31 
    32 class student :virtual public people
    33 {
    34 public:
    35     student(string name, int age) : people(name, age)
    36     {
    37     }
    38 };
    39 
    40 class doctor : public student, public teacher            
    41 {
    42 public:      
    43     doctor(string name, int age) : student(name, age), teacher(name, age) ,people(name,age)  //项目工程大的时候找顶层父类是很困难的
    44     {
    45     }
    46 };
    47 
    48 int main()
    49 {
    50     doctor d("mimi", 26);
    51 
    52     d.print();           // name =mimi,age =26  技术上解决多重继承带来的数据冗余,但是项目工程大的时候找顶层父类是很困难的
    53 
    54 
    55     //d.teacher::print();  // name =mimi,age =26
    56     //d.student::print();  // name =mimi,age =26
    57 
    58     return 0;
    59 }
     技术上解决多重继承带来的数据冗余,但是项目工程大的时候找顶层父类是很困难的-------增加了项目开发时间,代码复杂性增加

         虚继承在技术方面确实解决了数据冗余问题,但是它使程序变得过于复杂,不利于工程方面的应用。

      如果在项目中引进多重继承,当架构设计中需要继承时,无法确定使用直接继承还是虚继承。

      注意:多继承只适合做学术研究,不适合做工程开发

    小结:

    • C++支持多重继承的编程方式
    • 多重继承容易带来问题
      • 可能出现 “同一个对象的地址不同” 的情况 。
      • 虚继承可以解决数据冗余问题
      • 虚继承使得架构设计可能出现问题
  • 相关阅读:
    [计算机网络] 互联网协议栈(TCP/IP参考模型)各层的主要功能及相应协议
    [计算机网络-应用层] P2P应用
    [剑指Offer] 45.扑克牌顺子
    [剑指Offer] 44.翻转单词顺序列
    [STL] 如何将一个vector赋给另一个vector
    最近更新少,是因为在用框架做项目
    转收藏:Git常用命令速查表
    CentOS常用指令
    CentOS修改服务器系统时间
    Javascript定时跳转
  • 原文地址:https://www.cnblogs.com/liuyueyue/p/13397280.html
Copyright © 2020-2023  润新知