封装:封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项,或者叫接口。(数据安全机制)

有了封装,就可以明确区分内外,使得类实现者可以修改封装的东西而不影响部调用者;而外部调用者也可以知道自己不可以碰哪里。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

继承+多态:继承和多态必须一起说。一旦割裂,就说明理解上已经误入歧途了。

先说继承:继承同时具有两种含义:其一是继承基类的方法,并做出自己的改变和/或扩展——号称解决了代码重用问题;其二是声明某个子类兼容于某基类(或者说,接口上完全兼容于基类),外部调用者可无需关注其差别(内部机制会自动把请求派发[dispatch]到合适的逻辑)。(继承就是一种特殊的代码复用功能)

再说多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。(简单的理解 多态 == 覆盖和调用虚函数 )(多态比较复杂,各种骚操作都要用多态实现)

很显然,多态实际上是依附于继承的两种含义的:“改变”和“扩展”本身就意味着必须有机制去自动选用你改变/扩展过的版本,故无多态,则两种含义就不可能实现。

所以,多态实质上是继承的实现细节;那么让多态与封装、继承这两个概念并列,显然是不符合逻辑的。不假思索的就把它们当作可并列概念使用的人,显然是从一开始就被误导了——正是这种误导,使得大多数人把注意力过多集中在多态这个战术层面的问题上,甚至达到近乎恶意利用的程度;同时却忽略了战略层面的问题,这就致使软件很容易被他们设计成一滩稀屎(后面会详细谈论这个)。

image.png
image.png

  1. class Fish // base class
  2. {
  3. // ... Fish's members
  4. };
  5. class Carp:public Fish
  6. // derived class
  7. {
  8. // ... Carp's members
  9. };

鱼类世界呈现的一种简单的继承层次结构

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. class Fish
  5. {
  6. public:
  7. bool is_fresh_water_fish;
  8. void swim()
  9. {
  10. if (is_fresh_water_fish)
  11. {
  12. cout << "Swims in lake" << endl;
  13. }
  14. else
  15. {
  16. cout << "Swims in sea" << endl;
  17. }
  18. }
  19. };
  20. class Tuna: public Fish
  21. {
  22. public:
  23. Tuna(){
  24. is_fresh_water_fish = false;
  25. }
  26. };
  27. class Carp: public Fish
  28. {
  29. public:
  30. Carp()
  31. {
  32. is_fresh_water_fish = true;
  33. }
  34. };
  35. int main(){
  36. Carp my_lunch;
  37. Tuna my_dinner;
  38. cout << "About my food: " << endl;
  39. cout << "Lunch: ";
  40. my_lunch.swim();
  41. cout << "Dinner: ";
  42. my_dinner.swim();
  43. return 0;
  44. }

基类初始化—向基类传递参数

如果基类包含重载的构造函数,需要在实例化时给它提供实参,该如何办呢?创建派生对象时将 如何实例化这样的基类?方法是使用初始化列表,并通过派生类的构造函数调用合适的基类构造函数

  1. class Base
  2. {
  3. public: Base(int someNumber) // overloaded constructor
  4. {
  5. // Use someNumber
  6. }
  7. };
  8. Class Derived: public Base {
  9. public: Derived(): Base(25) // instantiate Base with argument 25
  10. {
  11. // derived class constructor code
  12. }
  13. };

对 Fish 类来说,这种机制很有用。通过给 Fish 的构造函数提供一个布尔输入参数,以初始化 Fish::isFreshWaterFish,可强制每个派生类都指出它是淡水鱼还是海水鱼

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. class Fish
  5. {
  6. protected:
  7. bool is_fresh_water_fish;
  8. public:
  9. Fish(bool is_fresh_water): is_fresh_water_fish(is_fresh_water){}
  10. void swim()
  11. {
  12. if (is_fresh_water_fish){
  13. cout << "Swims in lake" << endl;
  14. }
  15. else{
  16. cout << "Swims in sea" << endl;
  17. }
  18. }
  19. };
  20. class Tuna: public Fish
  21. {
  22. public:
  23. Tuna():Fish(false) {}
  24. };
  25. class Carp: public Fish
  26. {
  27. public:
  28. Carp():Fish(true){}
  29. };
  30. int main(){
  31. Carp my_lunch;
  32. Tuna my_dinner;
  33. cout << "About my food: " << endl;
  34. cout << "Lunch: ";
  35. my_lunch.swim();
  36. cout << "Dinner: ";
  37. my_dinner.swim();
  38. return 0;
  39. }

在派生类中覆盖基类的方法

如果派生类实现了从基类继承的函数,且返回值和特征标相同,就相当于覆盖了基类的这个方法, 如下面的代码所示:

  1. class Base
  2. {
  3. public:
  4. void DoSomething()
  5. {
  6. // implementation code… Does something
  7. }
  8. };
  9. class Derived:public Base
  10. {
  11. public:
  12. void DoSomething()
  13. {
  14. // implementation code… Does something else
  15. }
  16. };

因此,如果使用 Derived 类的实例调用方法 DoSomething( ),调用的将不是 Base 类中的这个方法。如果 Tuna 和 Carp 类实现了自己的 Swim( )方法,则在程序清单 10.3 的 main( )中下述代码将调用Tuna::Swim( )的实现,这相当于覆盖了基类 Fish 的方法 Swim( ):

在派生类中调用基类的方法

通常,Fish::Swim( )包含适用于所有鱼类(包括金枪鱼和鲤鱼)的通用实现。如果要在 Tuna::Swim( ) 和 Carp::Swim( )的实现中重用 Fish::Swim( )的通用实现,可使用作用域解析运算符(::),如下面的代码所示:

  1. class Carp: public Fish
  2. {
  3. public:
  4. Carp(): Fish(true) {}
  5. void Swim()
  6. {
  7. cout << "Carp swims real slow" << endl;
  8. Fish::Swim(); // invoke base class function using operator::
  9. }
  10. };

在基类方法和 main( )中,使用作用域解析运算符(::)来调用基类方法

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. class Fish
  5. {
  6. private:
  7. bool is_fresh_water_fish;
  8. public:
  9. Fish(bool is_fresh_water): is_fresh_water_fish(is_fresh_water){}
  10. void swim()
  11. {
  12. if (is_fresh_water_fish){
  13. cout << "Swims in lake" << endl;
  14. }
  15. else{
  16. cout << "Swims in sea" << endl;
  17. }
  18. }
  19. };
  20. class Tuna: public Fish
  21. {
  22. public:
  23. Tuna():Fish(false) {}
  24. void swim()
  25. {
  26. cout << "Tuna swims real fast, ";
  27. }
  28. };
  29. class Carp: public Fish
  30. {
  31. public:
  32. Carp():Fish(true){}
  33. void swim()
  34. {
  35. cout << "Carp swims real slow, ";
  36. Fish::swim();
  37. }
  38. };
  39. int main(){
  40. Carp my_lunch;
  41. Tuna my_dinner;
  42. cout << "About my food: " << endl;
  43. cout << "Lunch: ";
  44. my_lunch.swim();
  45. cout << "Dinner: ";
  46. my_dinner.swim();
  47. my_dinner.Fish::swim();
  48. return 0;
  49. }

image.png