C++深度解析(43)—被遗弃的多重继承(上)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_22847457/article/details/97039886

1.C++中的多重继承

  • C++支持编写多重继承的代码 
    • 一个子类可以拥有多个父类
    • 子类拥有所有父类的成员变量
    • 子类继承所有父类的成员函数
    • 子类对象可以当作任意父类对象使用(父子类有赋值兼容性原则)
  • 多重继承的语法规则

 
 
  1. class Derived : public BaseA,public BaseB,public BaseC{
  2. //````
  3. };
  • 多重继承的本质与单继承相同!多重继承容易带来问题,在其他语言已经被遗弃。

2.多重继承问题一

  • 通过多重继承得到的对象可以拥有“不同的地址”!!!
  • 解决方案:
  • 原因分析(不同的父类指针指向子类中属于自己的那部分地址)
  • 编程实验:多重继承问题一

 
 
  1. #include <iostream>
  2. using namespace std;
  3. class BaseA
  4. {
  5. int ma;
  6. public:
  7. BaseA( int a)
  8. {
  9. ma = a;
  10. }
  11. int getA()
  12. {
  13. return ma;
  14. }
  15. };
  16. class BaseB
  17. {
  18. int mb;
  19. public:
  20. BaseB( int b)
  21. {
  22. mb = b;
  23. }
  24. int getB()
  25. {
  26. return mb;
  27. }
  28. };
  29. // 多继承
  30. class Derived : public BaseA, public BaseB
  31. {
  32. int mc;
  33. public:
  34. Derived( int a, int b, int c) :BaseA(a), BaseB(b) // 初始化列表。
  35. {
  36. mc = c;
  37. }
  38. int getC()
  39. {
  40. return mc;
  41. }
  42. void print()
  43. {
  44. cout << "ma = " << getA() << ", "
  45. << "mb = " << getB() << ", "
  46. << "mc = " << mc << endl;
  47. }
  48. };
  49. int main()
  50. {
  51. cout << "sizeof(Derived) = " << sizeof(Derived) << endl; // 每个类私有变量的字节数加在一起
  52. Derived d(1, 2, 3);
  53. d.print();
  54. cout << "d.getA() = " << d.getA() << endl;
  55. cout << "d.getB() = " << d.getB() << endl; // 用类的对象访问成员方法
  56. cout << "d.getC() = " << d.getC() << endl;
  57. cout << endl;
  58. BaseA *pa = &d;
  59. BaseB *pb = &d; // 注意以上两行,都是将对象d的地址赋值给一个指针,表面上看似pa指针应该等于pb指针,
  60. // 但实际上不会这样,pa指向的是d对象中BaseA的子对象,而pb指向的是d对象中BaseB子对象的部分。(这两部分并未重合)
  61. cout << "pa->getA() = " << pa->getA() << endl; // 1
  62. cout << "pb->getB() = " << pb->getB() << endl; // 2
  63. cout << endl;
  64. void *paa = pa;
  65. void *pbb = pb;
  66. if (paa == pbb)
  67. {
  68. cout << "point to the same Object" << endl;
  69. }
  70. else
  71. {
  72. cout << "ERROR" << endl;
  73. }
  74. cout << "pa = " << pa << endl;
  75. cout << "pb = " << pb << endl;
  76. cout << "paa = " << paa << endl;
  77. cout << "pbb = " << pbb << endl;
  78. system( "pause");
  79. return 0;
  80. }
  • 运行结果

3.多重继承的问题二

  • 多重继承可能产生冗余的成员
  • eacher、Student都有People类所有成员变量成员函数,Doctor类同时继承Teacher类、Student类,不现实?

  • 编程实验:多重继承问题二

 
 
  1. /*多重继承产生的问题2*/
  2. #include <iostream>
  3. using namespace std;
  4. class People
  5. {
  6. string m_name;
  7. int m_age;
  8. public:
  9. People( string name, int age)
  10. {
  11. m_name = name;
  12. m_age = age;
  13. }
  14. void print()
  15. {
  16. cout << "Name = " << m_name << ", "
  17. << "Age = " << m_age << endl;
  18. }
  19. };
  20. class Teacher : public People
  21. {
  22. public:
  23. Teacher( string name, int age) : People(name, age)
  24. {
  25. }
  26. };
  27. class Student : public People
  28. {
  29. public:
  30. Student( string name, int age) : People(name, age)
  31. {
  32. }
  33. };
  34. class Doctor : public Teacher, public Student
  35. {
  36. public:
  37. Doctor( string name, int age) : Teacher(name + "1", age + 1), Student(name + "2", age + 2)
  38. {
  39. }
  40. };
  41. int main()
  42. {
  43. Doctor d("Nyist", 30);
  44. //d.print();//error
  45. d.Teacher::print(); //Name = Nyist1, Age = 31
  46. d.Student::print(); //Name = Nyist2, Age = 32
  47. return 0;
  48. }
  • 当多重继承关系出现闭合时将产生数据冗余的问题!!!! 
  • 解决方案:虚继承

  • 虚继承能够解决数据冗余问题
  • 中间层父类不用再关心顶层父类的初始化(但是中间层也要调用父类构造函数进行初始化。)
  • 最终子类必须直接调用顶层父类(以及中间父类)的构造函数

 
 
  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. class People
  5. {
  6. string mName;
  7. int mAge;
  8. public:
  9. People( string name, int age) // 构造函数
  10. {
  11. mName = name;
  12. mAge = age;
  13. }
  14. void print()
  15. {
  16. cout << "Name = " << mName << ", "
  17. << "Age = " << mAge << endl;
  18. }
  19. };
  20. // 先看看虚继承,然后再撤销虚继承,中间类采用虚继承
  21. class Teacher : virtual public People
  22. {
  23. public:
  24. Teacher( string name, int age) : People(name, age) // 构造函数,初始化列表
  25. {
  26. }
  27. };
  28. //中间类采用虚继承
  29. class Student : virtual public People
  30. {
  31. public:
  32. Student( string name, int age) : People(name, age)
  33. {
  34. }
  35. };
  36. // 博士类(一个博士可能即是老师,又是学生),采用直接继承,如果中间层的Teacher和Student不采用虚继承的话,
  37. // 那在Doctor类中将有来自People类的mName,mAge等成员变量各两份,出现数据冗余现象,而且在子类Doctor中,
  38. // 如果直接mName = 1, 会出现二义性的错误,因为编译器不知道mName到底是来自Teacher的还是Student类的。
  39. class Doctor : public Teacher, public Student
  40. {
  41. public:
  42. // 请注意,构造函数中的最后一个初始化People,也就是说采用虚继承的话,
  43. // 则最终的子类仍需调用顶层父类的构造函数,这也是虚继承的一大问题,因为在实际开发中,有时很难确定最顶层的父类。
  44. Doctor( string name, int age) :Teacher(name, age), Student(name, age), People(name, age)
  45. {
  46. }
  47. };
  48. // 各自私有属性是独立的
  49. int main()
  50. {
  51. Doctor d("SantaClaus", 25); // 先父类,在朋友,再自己
  52. d.print();
  53. system( "pause");
  54. return 0;
  55. }
  • 运行结果:
  • 依然存在的问题:当架构设计中需要继承时,无法确定使用直接继承还是虚继承!!

4.小结

  • C++支持多重继承的编程方式
  • 多重继承容易带来问题 
    • 可能出现“同一个对象的地址不同”的情况
    • 虚继承可以解决数据冗余的问题
    • 虚继承使得架构设计可能出现问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值