• 第48课 同名覆盖引发的问题


    父子间的赋值兼容:

    子类对象兼容性实验:

    第40行我们可以直接使用子类对象来初始化父类对象。这就是赋值兼容性。

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 class Parent
     7 {
     8 public:
     9     int mi;
    10     
    11     void add(int i)
    12     {
    13         mi += i;
    14     }
    15     
    16     void add(int a, int b)
    17     {
    18         mi += (a + b);
    19     }
    20 };
    21 
    22 class Child : public Parent
    23 {
    24 public:
    25     int mv;
    26     
    27     void add(int x, int y, int z)
    28     {
    29         mv += (x + y + z);
    30     }
    31 };
    32 
    33 int main()
    34 {
    35     Parent p;
    36     Child c;
    37     
    38     p = c;
    39     
    40     Parent p1(c);
    41     
    42     
    43     Parent& rp = c;
    44     Parent* pp = &c;
    45     
    46     rp.mi = 100;
    47     rp.add(5);             // 没有发生同名覆盖?
    48     rp.add(10, 10);        // 没有发生同名覆盖?
    49     
    50     /* 为什么编译不过? */
    51     // pp->mv = 1000;
    52     // pp->add(1, 10, 100);
    53     
    54     return 0;
    55 }

     47、48不会发生同名覆盖,因为rp本身就是父类类型的引用,只不过引用的对象是子类对象。47、48行会直接去父类中找add函数,所以不会报错。

    51、52行同时找不到mv和带三个参数的函数。这是因为pp是父类指针,它只会去父类中找对应的成员和函数。

    使用父类指针或者引用绝对不能访问子类中的成员和函数。

    特殊的同名函数:

     在子类中重定义父类中已经存在的函数(名字和参数都一样),这叫函数重写,是特殊的同名覆盖。

    函数重写是有必要的,因为对同一个函数,子类和父类可能表现出不同的动作。

    思考:

    当函数重写遇到赋值兼容会发生什么?

    示例:

    第47行的函数,如果子类中定义了print,如37行所示,则会打印39行的语句。将子类中的print函数注释掉,这时47行的函数调用的就是父类中的print,打印输出就如上图。

    父类对象调用print就会打印父类中的print:

    完善程序,添加全局打印函数:

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 class Parent
     7 {
     8 public:
     9     int mi;
    10     
    11     void add(int i)
    12     {
    13         mi += i;
    14     }
    15     
    16     void add(int a, int b)
    17     {
    18         mi += (a + b);
    19     }
    20     
    21     void print()
    22     {
    23         cout << "I'm Parent." << endl;
    24     }
    25 };
    26 
    27 class Child : public Parent
    28 {
    29 public:
    30     int mv;
    31     
    32     void add(int x, int y, int z)
    33     {
    34         mv += (x + y + z);
    35     }
    36     
    37     void print()
    38     {
    39         cout << "I'm Child." << endl;
    40     }
    41 };
    42 
    43 void how_to_print(Parent* p)
    44 {
    45     p->print();
    46 }
    47 
    48 int main()
    49 {
    50     Parent p;
    51     Child c;
    52     
    53     how_to_print(&p);    // Expected to print: I'm Parent.
    54     how_to_print(&c);    // Expected to print: I'm Child.
    55     
    56     return 0;
    57 }

     结果如下:

    我们期望53、54行一个打印父类信息、一个打印子类信息,但是结果并不是这样,而是全部打印的是父类信息。

    问题分析:

    编译器的这种处理方法是合理的,但不是我们期望的,我们之所以在子类中重定义一个同名的函数版本,就是因为父类中的这个函数满足不了我们的需求。但是,运行发现,实际调用的还是父类中的版本。也就是当函数重写遇上赋值兼容就出现了问题。下一节我们探究解决方案。

    小结:

  • 相关阅读:
    HTTP状态码1XX深入理解
    小公司比较吃亏的两道微服务面试题
    趣谈IO多路复用的本质
    白话TCP/IP原理
    Windows 是最安全的操作系统
    白话linux操作系统原理
    google orgchart
    CSharp: itext7.* create pdf file
    javascript: iframe switchSysBar 左欄打開關閉,兼容各瀏覽器操作(edit)
    csharp: Cyotek.GhostScript.Pdf Conversion pdf convert image x64
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9575435.html
Copyright © 2020-2023  润新知