C++ Primer Plus 学习笔记 第十四章 保护继承 多重继承

谁践踏了优雅 2022-01-23 07:29 483阅读 0赞

保护继承

保护继承会将基类的共有成员和保护成员继承为派生类的保护成员

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70

如果派生保护或派生私有的,又希望在派生类之外使用该类的方法。咋搞?

  1. 新建函数,然后在函数中调用临时基类对象

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 1

第二种方法,

在声明中直接使用using声明使用基类的某个函数

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 2

这样不管const还是非const的版本都可以用。

以上方法只适用于继承而不是包含。

多重继承:

每个继承的基类都需要单独声明范围限定(public,private, protected)

多继承示例:

Worker0.h

  1. #ifndef WORKER0_H_
  2. #define WORKER0_H_
  3. #include <string>
  4. class Worker
  5. {
  6. private:
  7. std::string fullname;
  8. long id;
  9. public:
  10. Worker() : fullname("no one") {}
  11. Worker(const std::string & s, long n) : fullname(s), id(n) {}
  12. virtual ~Worker() = 0;
  13. virtual void Set();
  14. virtual void Show() const;
  15. };
  16. class Waiter: public Worker
  17. {
  18. private:
  19. int panache;
  20. public:
  21. Waiter() : Worker(), panache(0) {}
  22. Waiter(const std::string & s, long n, int p = 0)
  23. : Worker(s, n), panache(p) {}
  24. Waiter(const Worker & wk, int p = 0)
  25. : Worker(wk), panache(p) {}
  26. void Set();
  27. void Show() const;
  28. };
  29. class Singer : public Worker
  30. {
  31. protected:
  32. enum{other, alto, contralto, soprano, base, baritone, tenor};
  33. enum {Vtypes = 7};
  34. private:
  35. // 这里需要使用const声明 否则编译器会出警告
  36. static const char *pv[Vtypes];
  37. int voice;
  38. public:
  39. Singer() : Worker(), voice(other) {}
  40. Singer(const std::string & s, long n, int v = other)
  41. : Worker(s, n), voice(v) {}
  42. Singer(const Worker & wk, int v = other)
  43. : Worker(wk), voice(v) {}
  44. void Set();
  45. void Show() const;
  46. };
  47. #endif

Worker0.cpp

  1. #include "Worker0.h"
  2. #include <iostream>
  3. using std::cout;
  4. using std::cin;
  5. using std::endl;
  6. Worker::~Worker(){}
  7. void Worker::Set()
  8. {
  9. cout << "Enter worker's name: ";
  10. getline(cin, fullname);
  11. cout <<"Enter worker's ID: ";
  12. cin >> id;
  13. while(cin.get() != '\n')
  14. continue;
  15. }
  16. void Worker::Show() const
  17. {
  18. cout << "Name: " << fullname << "\n";
  19. cout << "Employee ID: " << id << "\n";
  20. }
  21. void Waiter::Set()
  22. {
  23. Worker::Set();
  24. cout << "Enter waiter's panache rating: ";
  25. cin >> panache;
  26. while(cin.get() != '\n')
  27. continue;
  28. }
  29. void Waiter::Show() const
  30. {
  31. cout << "Category: waiter\n";
  32. Worker::Show();
  33. cout << "Panache rating: " << panache << "\n";
  34. }
  35. // 这里需要使用const声明 否则编译器会出警告
  36. const char* Singer::pv[] = {"other", "alto", "contralto", "soprano", "bass", "baritone", "tenor"};
  37. void Singer::Set()
  38. {
  39. Worker::Set();
  40. cout << "Enter number for singer's vocal range:\n";
  41. int i;
  42. for (i = 0; i < Vtypes; i++)
  43. {
  44. cout << i << ": " << pv[i] <<" ";
  45. if (i % 4 == 3)
  46. cout << endl;
  47. }
  48. if (i % 4 != 0)
  49. cout <<endl;
  50. while (cin >> voice && (voice < 0 || voice >= Vtypes))
  51. cout << "Please enter a value >=0 and < " << Vtypes << endl;
  52. }
  53. void Singer::Show() const
  54. {
  55. cout << "Category: singer\n";
  56. Worker::Show();
  57. cout << "Vocal range: " << pv[voice] << endl;
  58. }

worktest.cpp

  1. #include <iostream>
  2. #include "Worker0.h"
  3. const int LIM = 4;
  4. int main()
  5. {
  6. Waiter bob("Bob Apple", 314L, 5);
  7. Singer bev("Beverly Hills", 522L, 3);
  8. Waiter w_temp;
  9. Singer s_temp;
  10. Worker* pw[LIM] = {&bob, &bev, &w_temp, &s_temp};
  11. int i;
  12. for (i =2; i < LIM; i++)
  13. pw[i]->Set();
  14. for (i = 0; i < LIM; i++)
  15. {
  16. pw[i]->Show();
  17. std::cout << std::endl;
  18. }
  19. return 0;
  20. }

运行结果

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 3

那么问题来了

多重继承后每个派生类中都会有基类的组件。然后派生类的派生类要调用基类的函数时就不知道要调用哪个了。因为这时候有多个基类组件。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 4

咋搞

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 5

或者 使用虚基类 共享一个基类对象

格式:

class Singer:: virtual public Woker{}; or class Singer:: public virtual Woker{};

虚基类会使得程序完成一些额外的运算。

用了虚基类的话 构造函数的规则就需要更改

派生类的派生类不直接调用基类的构造函数 而是调用指定父类的构造函数。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 6

然后在构造函数定义的时候代表的意思会有变化

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 7

也就是说子类通过初始化成员列表来调用父类的构造函数,父类在调用基类的构造函数 来初始化时行不通了

wk没办法这样传递到基类的构造函数。那这时候编译器会使用基类的默认构造函数。 那就不是我们想要的了。

那咋搞?

20190603104356982.png

显示的调用基类的构造函数初始化。这种方法只适用于虚基类 非虚基类这么干的话是非法的。

这是构造函数的问题, 还有一个问题就是同名函数,用哪个父类的?(基类其实也是父类, 嗯。。爷爷类吧)

一种方法是可以使用作用域解析运算符来显式指明用哪个父类的函数

20190603104721568.png

第二种方法是 在子类定义一个同名函数。然后在函数中指明调用哪个父类的同名函数

20190603104858677.png

但如果这时候需要用到多个父类的Show方法就会出问题。

因为多个父类方法都调用了基类的Show方法。 就会有数据重复处理或者输出(参考上面的程序示例的Show()方法)

咋搞?

一种方法:

试用模块化处理,而不是递增:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 8

20190603105507401.png

20190603105655733.png

另一种办法是吧所有的数据组件都设置为保护而不是私有。但是用保护方法能够更好的控制数据的访问。

Set()函数的问题和处理方法类似。

20190603105955396.png

程序示例:

workermi.h

  1. #ifndef WORKERMI_H_
  2. #define WORKERMI_H_
  3. #include <string>
  4. class Worker
  5. {
  6. private:
  7. std::string fullname;
  8. long id;
  9. protected:
  10. virtual void Data() const;
  11. virtual void Get();
  12. public:
  13. Worker() : fullname("no one"), id(0L) {}
  14. Worker(const std::string & s, long n) : fullname(s), id(n) {}
  15. virtual ~Worker() = 0;
  16. virtual void Set() = 0;
  17. virtual void Show() const = 0;
  18. };
  19. class Waiter: virtual public Worker
  20. {
  21. private:
  22. int panache;
  23. protected:
  24. void Data() const;
  25. void Get();
  26. public:
  27. Waiter() : Worker(), panache(0) {}
  28. Waiter(const std::string & s, long n, int p = 0)
  29. : Worker(s, n), panache(p) {}
  30. Waiter(const Worker & wk, int p = 0)
  31. : Worker(wk), panache(p) {}
  32. void Set();
  33. void Show() const;
  34. };
  35. class Singer : virtual public Worker
  36. {
  37. protected:
  38. enum{other, alto, contralto, soprano, base, baritone, tenor};
  39. enum {Vtypes = 7};
  40. void Data() const;
  41. void Get();
  42. private:
  43. static const char *pv[Vtypes];
  44. int voice;
  45. public:
  46. Singer() : Worker(), voice(other) {}
  47. Singer(const std::string & s, long n, int v = other)
  48. : Worker(s, n), voice(v) {}
  49. Singer(const Worker & wk, int v = other)
  50. : Worker(wk), voice(v) {}
  51. void Set();
  52. void Show() const;
  53. };
  54. class SingingWaiter : public Singer, public Waiter
  55. {
  56. protected:
  57. void Data() const;
  58. void Get();
  59. public:
  60. SingingWaiter() {}
  61. SingingWaiter(const std::string & s, long n, int p = 0, int v = other)
  62. : Worker(s, n), Waiter(s, n, p), Singer(s, n, v) {}
  63. SingingWaiter(const Worker & wk, int p = 0, int v = other)
  64. : Worker(wk), Waiter(wk, p), Singer(wk, v) {}
  65. SingingWaiter(const Waiter & wt, int p = 0)
  66. : Worker(wt), Waiter(wt, p), Singer(wt) {}
  67. SingingWaiter(const Singer & wt, int p = 0)
  68. : Worker(wt), Waiter(wt, p), Singer(wt) {}
  69. void Set();
  70. void Show() const;
  71. };
  72. #endif

workermi.cpp

  1. #include "Workermi.h"
  2. #include <iostream>
  3. using std::cout;
  4. using std::cin;
  5. using std::endl;
  6. Worker::~Worker(){}
  7. void Worker::Data() const
  8. {
  9. cout << "Name: " << fullname << endl;
  10. cout << "Employee ID: " << id << endl;
  11. }
  12. void Worker::Get()
  13. {
  14. getline(cin, fullname);
  15. cout << "Enter worker's ID: ";
  16. cin >> id;
  17. while (cin.get() != '\n')
  18. continue;
  19. }
  20. // Waiter methods
  21. void Waiter::Set()
  22. {
  23. cout << "Enter waiter's name: ";
  24. Worker::Get();
  25. Get();
  26. }
  27. void Waiter::Show() const
  28. {
  29. cout << "Category: waiter\n";
  30. Worker::Data();
  31. Data();
  32. }
  33. void Waiter::Data() const
  34. {
  35. cout << "Panache rating: " << panache << endl;
  36. }
  37. void Waiter::Get()
  38. {
  39. cout << "Enter waiter's panache rating: ";
  40. cin >> panache;
  41. while(cin.get() != '\n')
  42. continue;
  43. }
  44. char const * Singer::pv[Singer::Vtypes] = {"other", "alto", "contralto", "soprano", "bass", "baritone", "tenor"};
  45. void Singer::Set()
  46. {
  47. cout << "Enter singer's name: ";
  48. Worker::Get();
  49. Get();
  50. }
  51. void Singer::Show() const
  52. {
  53. cout << "Gategory: singer\n";
  54. Worker::Data();
  55. Data();
  56. }
  57. void Singer::Data() const
  58. {
  59. cout << "Vocal range: " << pv[voice] << endl;
  60. }
  61. void Singer::Get()
  62. {
  63. cout << "Enter number for singer's vocal range:\n";
  64. int i;
  65. for (i = 0; i < Vtypes; i++)
  66. {
  67. cout << i << ": " << pv[i] << " ";
  68. if (i % 4 == 3)
  69. cout << endl;
  70. }
  71. if ( i % 4 != 0)
  72. cout << '\n';
  73. cin >> voice;
  74. while(cin.get() != '\n')
  75. continue;
  76. }
  77. void SingingWaiter::Data() const
  78. {
  79. Singer::Data();
  80. Waiter::Data();
  81. }
  82. void SingingWaiter::Get()
  83. {
  84. Waiter::Get();
  85. Singer::Get();
  86. }
  87. void SingingWaiter::Set()
  88. {
  89. cout << "Enter singing waiter's name: ";
  90. Worker::Get();
  91. Get();
  92. }
  93. void SingingWaiter::Show() const
  94. {
  95. cout << "Category: singing waiter\n";
  96. Worker::Data();
  97. Data();
  98. }

workmi.cpp

  1. #include <iostream>
  2. #include <cstring>
  3. #include "workermi.h"
  4. const int SIZE = 5;
  5. int main()
  6. {
  7. using std::cin;
  8. using std::cout;
  9. using std::endl;
  10. using std::strchr;
  11. Worker * lolas[SIZE];
  12. int ct;
  13. for (ct = 0; ct < SIZE; ct++)
  14. {
  15. char choice;
  16. cout << "Enter the employee category:\n"
  17. << "w: waiter s: singer "
  18. << "t: singing waiter q: quit\n";
  19. cin >> choice;
  20. while(strchr("wstq", choice) == NULL)
  21. {
  22. cout << "Please enter a w, s, t, or q: ";
  23. cin >> choice;
  24. }
  25. if(choice == 'q')
  26. break;
  27. switch(choice)
  28. {
  29. case 'w': lolas[ct] = new Waiter;
  30. break;
  31. case 's': lolas[ct] = new Singer;
  32. break;
  33. case 't': lolas[ct] = new SingingWaiter;
  34. break;
  35. }
  36. cin.get();
  37. lolas[ct] -> Set();
  38. }
  39. cout << "\nHere is your staff:\n";
  40. int i;
  41. for(i = 0; i < ct; i++)
  42. {
  43. cout << endl;
  44. lolas[i]->Show();
  45. }
  46. for(i = 0; i < ct; i++)
  47. delete lolas[i];
  48. cout << "Bye.\n";
  49. return 0;
  50. }

多重继承的其他变态情况

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 9

咋处理:

如果是多重继承的话 C++的二义性规则会发生变化

优先使用派生类的同名成员

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 10

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 11

类C的q()优于类B中的q() 但是 两个omg()会产生二义性。因为他们C和E是平级。

20190603131721915.png

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NvdWx3eWI_size_16_color_FFFFFF_t_70 12

发表评论

表情:
评论列表 (有 0 条评论,483人围观)

还没有评论,来说两句吧...

相关阅读