1.声明多重继承的方式

    • class D:public A,private B,protected C

    {类D新增加的成员}

    • D是多重继承的派生类
    • 以公用继承方式继承A类,
    • 以私有继承方式继承B类,
    • 以保护继承方式继承C类。
    • D按不同的继承方式的规则继承A,B,C的属性,确定各基类的成员在派生类中的访问权限。


    2.多重继承派生类的构造函数**

    • 多重继承派生类的构造函数形式与单继承时的构造函数形式基本相同,在初始表中包含多个基类构造函数。

    如:派生类构造函数名**(总参数表列): 基类1构造函数(参数表列),
    基类2构造函数(参数表列), 基类3构造函数 (参数表列**)
    {**派生类中新增数成员据成员初始化语句**}

    • 派生类构造函数的执行顺序同样为: 先调用基类的构造函数,再执行派生类构造函数的函数体。
    • 调用基类构造函数的顺序是按照声明派生类时基类出现的顺序。
    • 例:声明一个教师(Teacher)类和一个学生(Student)类,用多重继承的

    方式声明一个研究生(Graduate)派生类。

    1. #include <iostream>
    2. #include <string>
    3. using namespace std;
    4. class Teacher//声明类Teacher(教师)
    5. {public: //公用部分
    6. Teacher(string nam,int a, string t) //构造函数
    7. {name=nam;
    8. age=a;
    9. title=t;}
    10. void display( ) //输出教师有关数据
    11. {cout<<″name:″<<name<<endl;
    12. cout<<″age″<<age<<endl;
    13. cout<<″title:″<<title<<endl; }
    14. protected: //保护部分
    15. string name;
    16. int age;
    17. string title; //职称
    18. };
    19. class Student //定义类Student(学生)
    20. {public: Student(char nam[],char s,float sco)
    21. {strcpy(name1,nam);
    22. sex=s;
    23. score=sco;} //构造函数
    24. void display1( ) //输出学生有关数据
    25. {cout<<″name:″<<name1<<endl;
    26. cout<<″sex:″<<sex<<endl;
    27. cout<<″score:″<<score<<endl; }
    28. protected: //保护部分
    29. string name1;
    30. char sex;
    31. float score; //成绩
    32. };
    33. class Graduate:public Teacher,public Student //声明多重继承的派生类 Graduate
    34. {public: Graduate(string nam,int a,char s, string t,float sco,float w): Teacher(nam,a,t),Student(nam,s,sco),wage(w) { }
    35. void show( ) //输出研究生的有关数据
    36. {cout<<″name:″<<name<<endl;
    37. cout<<″age:″<<age<<endl;
    38. cout<<″sex:″<<sex<<endl;
    39. cout<<″score:″<<score<<endl;
    40. cout<<″title:″<<title<<endl;
    41. cout<<″wages:″<<wage<<endl; }
    42. private:
    43. float wage; //工资
    44. };
    45. int main( )
    46. {Graduate grad1(″Wang-li″,24,′f′,″assistant″,89.5,1234.5);
    47. grad1.show( );
    48. return 0;
    49. }
    • 在多重继承时,从不同的基类中会继承一些**重复的数据**
      • 解决方法: 在两个基类中可以都使用同一个数据成员名name,而在**引 **

    用数据成员时指明其作用域**,如:**

    1. cout<<″name:″<<Teacher::name<<endl;

    3.多重继承引起的二义性问题

    • 如果类A和类B中都有成员函数display和数据成员a,类C是类A和类B的直接派生类。
      • 讨论下列3种情况:
        • 两个基类有同名成员。
        • 两个基类和派生类三者都有同名成员。
        • 类A和类B是从同一个基类派生的。
    • 两个基类有同名成员

    image.png

    1. class A
    2. {public:
    3. int a;
    4. void display( );
    5. };
    6. class B
    7. {public:
    8. int a;
    9. void display( );
    10. };
    11. class C :public A,public B
    12. {public :
    13. int b;
    14. void show();
    15. };
    • 在main函数中

      • 可以用基类名来限定:

        1. c1.A::a=3;//引用c1对象中的基类A的数据成员a
        2. c1.A::display(); //调用c1对象中的基类A的成员函数display
      • 如果是在派生类C中通过派生类成员函数show访问基类A

    的display和a,可以不必写对象名而直接写

    1. void C::show( ){
    2. A::a=3;//指当前对象
    3. A::display( );}
    • 两个基类和派生类三者都有同名成员。

      1. class C :public A,public B
      2. {int a;
      3. void display();
      4. };
      • 在main函数中

        1. C c1;
        2. c1.a=3;
        3. c1.display( );
        • 基类的同名成员在派生类中被屏蔽,成为“不可见”的,派生类新增加的同名成员覆盖了基类中的同名成员。
        • 不同的成员函数,只有在函数名和参数个数相同、类型相匹配的情况下才发生同名覆盖,否则为函数重载。
        • 要在派生类外访问基类A中的成员,应指明作用域A:
          1. c1.A::a=3;
          2. c1.A::display();
    • 如果类A和类B是从同一个基类派生的

    image.png

    1. class N
    2. {public:
    3. int a;
    4. void display()
    5. {cout<<″A::a=”<<a<<endl;}
    6. };
    7. class A:public N
    8. {public:
    9. int a1;
    10. };
    11. class B:public N
    12. {public:
    13. int a2;
    14. };
    15. class C :public A,public B
    16. {public :
    17. int a3;
    18. void show( )
    19. {cout<<″a3=″<<a3<<endl;}
    20. };
    21. int main( )
    22. {C c1;//定义C类对象c1
    23. }
    • 怎样才能访问类A中从基类N继承下来的成员呢?

      1. c1.a=3; c1.display( ); c1.N::a=3; c1.N::display(); //ERROR
      • 应当通过类N的直接派生类名来指出要访问的是类N的哪一个派生类中的基类成员。
        1. c1.A::a=3; c1.A::display();

    4.虚基类

    • 虚基类的作用:C++提供虚基类(virtual base class)的方法,**使得在继承间接共同基类时只保留一份成员**。
    • 声明虚基类的一般形式:**class 派生类名: virtual 继承方式 基类名**
      • 虚基类并不是在声明基类时声明的,而是在声明派生类时,**指定继承方式时声明**的。
      • 声明虚基类后,当基类通过多条派生路径被一个派生类继承时,该**派生类只继承该基类一次**
      • 为了保证虚基类在派生类中只继承一次,应当在该基类的**所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。**

    **

    • 虚基类的初始化

      • 例:

        1. class A//定义基类A
        2. {A(int i){ } //基类构造函数,有一个参数
        3. …};
        4. class B :virtual public A //A作为B的虚基类
        5. {B(int n):A(n){ } //B类构造函数,在初始化表中对虚基类初始化
        6. …};
        7. class C :virtual public A //A作为C的虚基类
        8. {C(int n):A(n){ } //C类构造函数,在初始化表中对虚基类初始化
        9. …};
        10. class D :public B,public C //类D的构造函数,在初始化表中对所有基类初始化
        11. {D(int n): A(),B(n),C(n){ }
        12. …};
      • 如果在虚基类中定义了带参数的构造函数,而且没有定义默认构造函数,则在其所有派生类中,通过**构造函数的初始化表对虚基类进行初始化**。

      • 在定义类D的构造函数时,在最后的派生类中不仅要负责对其直接基类进行初始化,还要**负责对虚基类初始化**。
      • C++编译系统只执行**最后的派生类虚基类的构造函数的调用**,而忽略虚基类的其他派生类(如类B和类C) 对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。

    **

    • 例:在Teacher类和Student类之上增加一个共同的基类Person,作为人员的一些基本数据都放在Person中,在Teacher类和Student类中再增加一些必要的数据。

      1. #include <iostream>
      2. #include <string>
      3. using namespace std;
      4. class Person //声明公共基类Person
      5. {public:
      6. Person(string nam,char s,int a)//构造函数
      7. {name=nam;sex=s;age=a;}
      8. protected: //保护成员
      9. string name;
      10. char sex;
      11. int age;
      12. };
      13. //声明Person的直接派生类Teacher
      14. class Teacher:virtual public Person //声明Person为公用继承的虚基类
      15. {public: Teacher(string nam,char s,int a, string t):Person(nam,s,a)//构造函数 {title=t;
      16. }
      17. protected: //保护成员
      18. string title; //职称
      19. };
      20. class Student:virtual public Person
      21. {public:
      22. Student(string nam,char s,int a,float sco) //构造函数
      23. :Person(nam,s,a),score(sco){ } //初始化表
      24. protected: //保护成员
      25. float score; //成绩
      26. };
      27. class Graduate:public Teacher,public Student
      28. {public:
      29. Graduate(string nam,char s,int a, string t,float sco,float w)//构造函数
      30. :Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco),wage(w){}
      31. void show( ) //输出研究生的有关数据
      32. {cout<<″name:″<<name<<endl;
      33. cout<<″age:″<<age<<endl;
      34. cout<<″sex:″<<sex<<endl;
      35. cout<<″score:″<<score<<endl;
      36. cout<<″title:″<<title<<endl;
      37. cout<<″wages:″<<wage<<endl;
      38. }
      39. private:
      40. float wage; //工资
      41. };
      42. int main( )
      43. {Graduate grad1(″Wang-li″,′f′,24,″assistant″,89.5,1234.5);
      44. grad1.show( );
      45. return 0;
      46. }
      • 不提倡在程序中使用多重继承,能用单继承解决的问题就不要使用多重继承,只有在比较简单和不易出现二义性的情况或实在必要时才使用多重继承 。