C/C++日常学习总结(第四篇)共同基类产生的二义性和继承与组合的区别

转载自“https://blog.csdn.net/wu_123_456/article/details/24418841”

1.共同基类派生产生的二义性及解决办法?


 
 
  1. <span style= "font-size:14px;">#include <iostream>
  2. using namespace std;
  3. class A //公共基类
  4. {
  5. protected: //protected成员列表
  6. int x;
  7. public: //public成员列表
  8. A( int xp = 0) //构造函数
  9. {
  10. x = xp;
  11. }
  12. void SetX(int xp) //设置protected成员x的值
  13. {
  14. x = xp;
  15. }
  16. void print()
  17. {
  18. cout << "this is x in A: " << x << endl;
  19. }
  20. };
  21. //class B: virtual public A
  22. class B: public A //类B由类A派生而来
  23. {
  24. };
  25. //class C: virtual public A //类C由类A派生而来
  26. class C: public A //类C由类A派生而来
  27. {
  28. };
  29. class D : public B, public C //类D由类B和类C派生而来
  30. {
  31. };
  32. int main()
  33. {
  34. D d; //声明一个D类对象exD
  35. d.SetX( 5); //SetX()具有二义性, 系统不知道是调用B类的还是C类的SetX()函数
  36. d.print(); //print()具有二义性, 系统不知道是调用B类的还是C类的print()函数
  37. return 0;
  38. }</span>


解决办法:

使用关键字virtual将共同基类A声明为虚基类,例如class B:virtual Public A{};      class C:virtual Public A{};

D中对于这两个函数只有一个备份,也可通过上次说的成员名限定,d.A::print();d.B::print();d.C::print();

 

2.虚基派生二义性与多基派生二义性不同?

(1.)多基派生说白了就是不同基类里面有相同的函数名,即函数名的二义性,通过加域名限定符来解决。

(2.)虚基派生是同一基类的多重拷贝,即存储的二义性,通过加virtual来解决。

 

3.虚基派生的构造函数的调用关系。


 
 
  1. <span style= "font-size:12px;">#include <iostream>
  2. using namespace std;
  3. class A //类A定义
  4. {
  5. private: //private成员列表
  6. int x;
  7. public: //public成员列表
  8. A( int xp = 0) //构造函数,带缺省参数
  9. {
  10. x=xp;
  11. cout<< "A的构造函数被执行"<< endl;
  12. }
  13. ~A() //析构函数
  14. {
  15. cout<< "A的析构函数被执行"<< endl;
  16. }
  17. void Print() //显示成员变量x的值
  18. {
  19. cout << x << endl;
  20. }
  21. };
  22. class B: virtual public A //类B由类A虚基派生而来
  23. {
  24. public:
  25. B( int xp):A(xp) //在初始化表中调用基类构造函数
  26. {
  27. cout<< "B的构造函数被执行"<< endl;
  28. }
  29. ~B() //析构函数
  30. {
  31. cout<< "B的析构函数被执行"<< endl;
  32. }
  33. };
  34. class C: virtual public A //类C由类A虚基派生而来
  35. {
  36. public:
  37. C( int xp):A(xp) //在初始化表中调用基类构造函数
  38. {
  39. cout<< "C的构造函数被执行"<< endl;
  40. }
  41. ~C() //析构函数
  42. {
  43. cout<< "C的析构函数被执行"<< endl;
  44. }
  45. };
  46. class D: public B, public C //类D由类B和类C共同派生而来
  47. {
  48. public:
  49. D( int xp):B(xp),C(xp),A(xp) //初始化表中不仅要调用B类和C类的构造函数,还应调用共同虚基类的构造函数A(xp)
  50. {
  51. cout<< "D的构造函数被执行"<< endl;
  52. }
  53. ~D() //析构函数
  54. {
  55. cout<< "D的析构函数被执行"<< endl;
  56. }
  57. };
  58. int main()
  59. {
  60. D d(2); //声明D类对象d
  61. d.Print(); //结果为2。如果去掉类D的构造函数的初始化列表中的A(xp),则结果为0。好好体会!!!!!!!!
  62. cout << endl;
  63. B b(3);
  64. b.Print(); //结果为3。如果去掉类B的构造函数的初始化列表中的A(xp),则结果为0。好好体会!!!!!!!!
  65. cout << endl;
  66. return 0; //main函数执行完毕退出后,d销毁,析构函数触发执行
  67. }</span>

大家可能会问:

(1.)定义一个D的对象,岂不是A的构造函数要调用两次?

(2.)d.Print(); //结果为2。如果去掉类D的构造函数的初始化列表中的A(xp),则结果为0。这是为什么?

【解析】:

  实际情况是:
  B(总参数表):A(参数表)
  C(总参数表):A(参数表)
  D(总参数表):B(参数表),C(参数表),A(参数表)
  根据虚基派生的性质,类D中只有一份虚基类A的拷贝,因此A类的构造函数在D类中只能被调用一次。所以,从A类直接派生(B和C)和间接派生(D)的类中,其构造函数的初始化列表中都要列出对虚基类A构造函数的调用。这种机制保证了不管有多少层继承,虚基类的构造函数必须且只能被调用一次。

4.继承和组合的区别?贴一段组合的代码,一看就明白了。


 
 
  1. #include <iostream>
  2. using namespace std;
  3. class Eye
  4. {
  5. public:
  6. void Look() { cout << "Eye.Look()." << endl;}
  7. };
  8. class Nose
  9. {
  10. public:
  11. void Smell() { cout << "Nose.Smell()." << endl;}
  12. };
  13. class Mouth
  14. {
  15. public:
  16. void Eat() { cout << "Mouth.Eat()." << endl;}
  17. };
  18. class Ear
  19. {
  20. public:
  21. void Listen() { cout << "Ear.Listen()." << endl;}
  22. };
  23. //组合方式:逻辑很清晰,后续扩展很方便。
  24. class Head
  25. {
  26. private:
  27. Eye m_eye;
  28. Nose m_nose;
  29. Mouth m_mouth;
  30. Ear m_ear;
  31. public:
  32. void Look()
  33. {
  34. m_eye.Look();
  35. }
  36. void Smell()
  37. {
  38. m_nose.Smell();
  39. }
  40. void Eat()
  41. {
  42. m_mouth.Eat();
  43. }
  44. void Listen()
  45. {
  46. m_ear.Listen();
  47. }
  48. };


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值