35 同名覆盖引发的问题

1 父子间的赋值兼容

  • 子类对象可以当作父类对象使用(兼容性)

    • 子类对象可以直接赋值给父类对象
    • 子类对象可以直接初始化父类对象
    • 父类指针可以直接指向子类对象:多态
    • 父类引用可以直接引用子类对象
  • 示例:子类对象的兼容性

    • Demo

      #include <iostream>
      #include <string>
      
      using namespace std;
      
      class Parent
      {
      public:
          int mi;
          
          void add(int i)
          {
              mi += i;
          }
          
          void add(int a, int b)
          {
              mi += (a + b);
          }
      };
      
      class Child : public Parent
      {
      public:
          int mv;
          
          void add(int x, int y, int z)
          {
              mv += (x + y + z);
          }
      };
      
      int main()
      {
          Parent p;
          Child c;
          
          p = c;
          
          Parent p1(c);
          
          
          Parent& rp = c;  //父类引用:引用子类对象
          Parent* pp = &c;  //父类指针:指向子类对象
          
          rp.mi = 100;
          rp.add(5);             // 没有发生同名覆盖
          rp.add(10, 10);        // 没有发生同名覆盖
          
          //编译错误
          pp->mv = 1000;
          pp->add(1, 10, 100);
          
          return 0;
      }
      
    • 编译

      test.cpp: In fuction 'int main()':
      test.cpp:51: error: 'class Parent' has no member named 'mv'
      test.cpp:52: error: no matching function for call to 'Parent::add(int,int,int)'
      test.cpp:11:note: candidates are: void Parent::add(int)
      test.cpp:16:note:                 void Parent::add(int,int)
      
  • 当使用父类指针(引用)指向子类对象时

    • 子类对象退化为父类对象
    • 只能访问父类中定义的成员
    • 可以直接访问被子类覆盖的同名成员,如父类的 add(int)add(int,int) 被子类的 add(int,int,int) 所覆盖

2 特殊的同名函数

  • 子类中可以重定义父类中已经存在的成员函数:重定义的原因是父类中的函数不能满足使用

  • 这种定义发生在继承中,叫做函数重写

  • 函数重写是同名覆盖的一种特殊情况

    class Parent
    {
    public:
        void print()
        {
            cout << "I'm Parent." << endl;
        }
    };
    
    //函数重写
    class Child : public Parent
    {
    public:
        //函数原型与父类中的完全相同,此处函数重写是必须的
        void print()
        {
            cout << "I'm Child." << endl;
        }
    };
    
  • 问题:当函数重写遇到赋值兼容会发生什么?

  • 示例:函数重写 VS 赋值兼容

    • Demo

      #include <iostream>
      #include <string>
      
      using namespace std;
      
      class Parent
      {
      public:
          int mi;
          
          void add(int i)
          {
              mi += i;
          }
          
          void add(int a, int b)
          {
              mi += (a + b);
          }
          
          //打印父类信息
          void print()
          {
              cout << "I'm Parent." << endl;
          }
      };
      
      class Child : public Parent
      {
      public:
          int mv;
          
          void add(int x, int y, int z)
          {
              mv += (x + y + z);
          }
          
          //重写函数:打印子类信息
          void print()
          {
              cout << "I'm Child." << endl;
          }
      };
      
      void how_to_print(Parent* p)
      {
          p->print();
      }
      
      int main()
      {
          Parent p;
          Child c;
          
          p.print();
          c.print();
          
          how_to_print(&p);    // Expected to print: I'm Parent.
          how_to_print(&c);    // Expected to print: I'm Child.
          
          return 0;
      }
      
    • 编译运行

      I'm Parent.
      I'm Child.
      
      I'm Parent.
      I'm Parent.  !!!!!!
      
  • 问题分析

    • 编译期间,编译器只能根据指针的类型判断所指向的对象

    • 根据赋值兼容,编译器认为父类指针指向的是父类对象

    • 因此,编译结果只可能是调用父类中定义的同名函数

    • 在编译下面的函数时,编译器不可能知道指针 p 究竟指向了什么,但是编译器没有理由报错。于是编译器认为最安全的做法是调用父类的 print 函数,因为父类和子类肯定都有相同的 print 函数。但不是期望的! => 使用多态解决

      void how_to_print(Parent* p)
      {
          p->print();
      }
      
posted @ 2020-10-30 19:40  nxgy  阅读(70)  评论(0编辑  收藏  举报