多态

1. 多态的概念

1.1. 现实世界中的多态现象

  • 现实世界存在大量的一对多现象
  • 一词多义
  • 一题多解

    1.2. C++中的多态

  • C++中的多态是对现实中多态现象的模拟

  • 一种接口,多种实现
  • 虚函数:

    • 用virtual关键字修饰的成员函数称为虚函数
      1. virtual void f()
  • 重写:

    • 子类新增的方法与父类的虚方法同名同参同常同返回值 称为重写,又叫覆盖。
    • 子类重写父类的虚函数,子类的成员函数将自动成为虚函数,可以不用virtual显式生么,但最好显示声明一下
    • 重写的要点:
      • 1> 子类与父类之间
      • 2> 父类方法为虚方法
      • 3> 同名同参同常同返回值,所谓“同常”,即函数(方法)被const修饰一致
    • 隐藏的要点:
      • 子类与父类之间
      • 同名
    • 重载的要点:
      • 同一作用域,同一类或者全局作用域
      • 同名不同参或者同名同参不同常,返回值随意

2. 多态程序设计要点

  • 父类的成员函数用virtual声明
  • 子类重写父类的虚成员函数
  • 父类的引用或者指针指向子类对象;
  • 通过父类引用或者指针调用该虚方法时,将执行子类中的实现;
  • 示例代码: ```cpp

    include

    using namespace std;

class Animal{ public: virtual void move(){ cout << “move move …” << endl; } };

class Bird : public Animal{ public: void move(){ cout << “fly fly …” << endl; }

};

class Horse : public Animal{ public: void move(){ cout << “dadada …” << endl; }

};

void escape(Animal& animal){ animal.move(); }

void escape(Animal* panimal){ panimal->move(); }

int main(){ Animal a; Bird b; Horse h;

  1. escape(a);
  2. escape(b);
  3. escape(h);
  4. cout << "-------------" << endl;
  5. escape(&a);
  6. escape(&b);
  7. escape(&h);
  8. return 0;

}

  1. <a name="RBsVK"></a>
  2. # 3. 动态绑定
  3. - 动态绑定是多态的实现原理
  4. - 函数绑定:将函数调用语句解释为执行特定函数实现代码块
  5. - 动态绑定:使用指针或者引用调用虚方法时,根据指针或者引用所指对象的实际类型的来执行相应的虚方法的实现
  6. - 静态绑定:
  7. - 任何对非虚方法的绑定都是静态绑定,不论是使用指针,还是使用引用,还是对象名去调用
  8. - 任何使用对象名调用的方法都是静态绑定,不论方法是否为虚方法
  9. <a name="xsu03"></a>
  10. ## 4. 运行时类型识别(RTTI)
  11. <a name="u59qW"></a>
  12. ## 4.1. 定义
  13. 由于存在多态,有时父类指针或者引用的实际类型需要在程序运行时才能确定,在程序运行的过程中判断父类指针或者引用的实际类型称为 **_动态类型识别_**
  14. <a name="bx5ah"></a>
  15. ## 4.2. typeid运算符
  16. - 头文件<typeinfo>
  17. - typeid(类型) 或者 typeid(对象名)
  18. - 返回type_info类的对象,用于描述类型信息
  19. - type_info类的方法
  20. - name() --- 返回字符串形式类型信息
  21. - ==和!= --- 直接进行类型比较
  22. - 用法:
  23. ```cpp
  24. if(typeid(*父类指针)== typeid(子类类型)){
  25. }
  • 示例代码: ```cpp

    include

    include

    using namespace std;

class Animal{};

class Pen{};

int main(){ / type_info ti = typeid(888); cout << ti.name() << endl; /

  1. cout << typeid(888).name() << endl;
  2. cout << typeid(int).name() << endl;
  3. cout << typeid(3.14).name() << endl;
  4. cout << typeid(double).name() << endl;
  5. cout << typeid(Animal).name() << endl;
  6. cout << typeid(Pen).name() << endl;
  7. Animal a;
  8. cout << typeid(a).name() << endl;
  9. cout << (typeid(a) == typeid(Pen)) << endl;
  10. return 0;

}

  1. <a name="M1unk"></a>
  2. ## 4.3. dynamic_cast运算符
  3. - 用于具有虚函数的父类指针或引用进行向下转型
  4. - 用法:
  5. - dynamic_cast<子类引用或指针>(父类引用或指针);
  6. - 类型检查机制:
  7. - 如果父类引用或指针所指向的实际类型与子类类型一致,则转换成功,返回实际类型的引用或者指针;否则转换失败,引用则抛出异常"bad_cast",指针则返回NULL.
  8. - 通过返回值是否为NULL判断实际类型
  9. - 注意
  10. - 父类必须具有虚函数
  11. - 在工程上常把父类的析构函数定义为虚函数,从而满足要求。
  12. <a name="owhQE"></a>
  13. ## 4.4. reinterpret_cast运算符
  14. - reinterpret_cast<子类指针>(父类指针)
  15. - 转换时不做类型检查,不安全。
  16. <a name="VgJ1v"></a>
  17. ## 4.5 示例代码:
  18. ```cpp
  19. #include <iostream>
  20. #include <typeinfo>
  21. using namespace std;
  22. class Animal{
  23. public:
  24. virtual void move(){
  25. cout << "move move ..." << endl;
  26. }
  27. };
  28. class Bird : public Animal{
  29. public:
  30. void move(){
  31. cout << "fly fly ..." << endl;
  32. }
  33. void eatWorm(){
  34. cout << "delicious worm ..." << endl;
  35. }
  36. };
  37. class Horse : public Animal{
  38. public:
  39. void move(){
  40. cout << "dadada ..." << endl;
  41. }
  42. };
  43. int main(){
  44. Animal* p = NULL;
  45. int n;
  46. while(1){
  47. cout << "请输入: 1 - Animal, 2 - Bird, 3 - Horse : " << endl;
  48. cin >> n;
  49. switch(n){
  50. case 1 :
  51. p = new Animal;
  52. break;
  53. case 2 :
  54. p = new Bird;
  55. break;
  56. case 3 :
  57. p = new Horse;
  58. }
  59. p->move();
  60. if(typeid(*p) == typeid(Bird)){
  61. Bird* bird = reinterpret_cast<Bird*>(p);
  62. bird->eatWorm();
  63. }
  64. if(Bird* bird = dynamic_cast<Bird*>(p)){
  65. bird->eatWorm();
  66. }
  67. if(Bird* bird = reinterpret_cast<Bird*>(p)){
  68. bird->eatWorm();
  69. }
  70. }
  71. return 0;
  72. }

5. 抽象类

5.1. 纯虚函数

  • 纯虚函数的声明:virtual void f() = 0;
  • 纯虚函数没有函数体,不同于函数体为空。
  • 必须被重写的成员函数应该声明为纯虚函数。

    5.2. 抽象类

  • 存在纯虚函数的类称为抽象类

  • 抽象类只能做父类用于派生,不能被实例化
  • 子类如果不重写父类的全部纯虚函数,则子类也是抽象类。
  • 永远不需要实例化的类应该定义为抽象类
  • 抽象类的作用主要是做接口类
    • 接口类: 只有方法,没有数据

      5.3 示例代码

      ```cpp

      include

      using namespace std;

class Animal{ // 父类—派生—抽象类 public: virtual void move() = 0; // 纯虚函数声明 virtual void eat() = 0; };

class Bird : public Animal{ public: void move(){ cout << “fly fly …” << endl; }

    void eat(){
        cout << "bird eat.." << endl;
    }

};

class Horse : public Animal{ public: void move(){ cout << “dadada …” << endl; }

    void eat(){
        cout << "horse eat..." << endl;
    }

};

int main(){ Bird b; Horse h;

b.move();
b.eat();
h.move();
h.eat();

return 0;

} ```