zoukankan      html  css  js  c++  java
  • C++中的赋值兼容性和重写

    1,父子间的赋值兼容:

        1,子类对象可以当做父类对象使用(赋值兼容性):

           1,子类对象可以直接赋值给父类对象;

           2,子类对象可以直接初始化父类对象;

           3,父类指针可以直接指向子类对象(得到的是子类对象);

           4,父类引用可以直接引用子类对象;

          

    2,子类对象的兼容性编程实验:

     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);  // 这里 rp 指向子类,应该是不可以访问被同名覆盖的  add(int),但是没有报错;也就是可以访问 add(int),这里没有发生同名覆盖,子类对象已经变成父类对象,只能访问父类当中的成员;
    48     rp.add(10, 10);  // 这里 rp 指向子类,应该是不可以访问被同名覆盖的 add(int, int),但是没有报错;也就是可以访问add(int, int),这里没有发生同名覆盖,子类对象已经变成父类队像,只能访问父类中的成员;
    49     
    50     /* 为什么编译不过?子类对象已经变成父类对象,没办法访问子类对象中的成员 */
    51     // pp->mv = 1000;  // 'class Parent' has no member named 'mv' 
    52     // pp->add(1, 10, 100);  // no matching function for call to  'Parent::add(int, int, int)'
    53                              // candidates are: int Parent::add(int)
    54                              //                 int Parent::add(int, int)
    55     
    56     return 0;
    57 }

       

    3,当使用父类指针(引用)指向子类对象时:

        1,子类对象完全退化为父类对象;   

        2,只能访问父类中定义的成员;

           1,通过这个指针或者引用只能访问父类当中的成员,绝对不可以访问子类当中的成员;

        3,可以直接访问被子类覆盖的同名成员;

       

    4,特殊的同名函数:

        1,子类中可以重定义父类中已经存在的成员函数;

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

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

     

           1,子类当中定义的函数原型和父类当中定义的函数原型一模一样,这叫函数重写;

           2,意味着在子类当中重新实现了父类当中的函数;

           3,有必要重写实现吗?

               1,有必要,如果定义了子类对象,调用了 print() 后,显然的期望打印的是 "I'm Child",证明自己是子类对象,如果打印的是 "I'm   Paretn."就会很奇怪,明明是子类对象,但是却打印父类信息;

               2,所以这个函数到了子类当中必须重写,必须重定义,不能再用父类中的函数版本了;

           4,以后这种情况统一叫做“函数重写”;

       

    5,思考:

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

           1,只能是退化为父类指针或引用;

       

    6,赋值兼容的问题编程实验:

     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();  // 当程序编译到这里了,需要通过 p 指针调用 print() 函数,编译器不知道参数 p 指针指向的是父类对象还是子类对象,因为程序还没运行,实参数还没传进来;于是编译器这样假设认为:根据赋值兼容性原则,p 指针所指向的都是父类对象;
    46 }
    47 
    48 int main()
    49 {
    50     Parent p;
    51     Child c;
    52  
    53     p.print();   // 打印 I'm Parent.   
    54     c.print();   // 当子类中不重写 print() 函数时,打印 I'm Parent.;当子类中重写 print() 函数时,打印 I'm Child.;因此重写很有必要; 
    55     
    56     how_to_print(&p);    // Expected to print: I'm Parent. 打印 I'm Parent;
    57     how_to_print(&c);    // Expected to print: I'm Child.  打印 I'm Parent;
    58     
    59     return 0;
    60 }

       

    7,父子间的赋值兼容:

        1,问题分析:

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

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

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

        2,实例分析:

    1 void how_to_print(Parent* p)

    2 {

    3 P->print();

    4 } 

           1,在编译这个函数的时候,编译器不可能知道指针 p 究竟指向了什么,但是编译器没有理由报错(因为单看这个函数语法实现没有错误);于是,编译器认为最安全的做法是调用父类的 print 函数,如果运行时实参是子类对象,根据赋值兼容性原则,将 p->print() 函数解释为父类函数也是合理的,父类和子类肯定都有相同的 print 函数;

        3,编译器的处理方法是合理的吗?是期望的吗?

           1,同名覆盖带来的问题,当子类重写了父类的某一个函数时,就有可能带来这样的问题;

           2,这种处理方法是合理的;

           3,这种结果不是我们期望的,之所以重写父类中的函数,是因为父类中的 函数版本不能满足我们的需求了,所以我们重写了父类中的版本,但是 运行的时候还是调用父类中实现的函数,显然不是我们期望的结果;

       

    8,小结:

        1,子类对象可以当作父类对象使用(赋值兼容);

           1,赋值或初始化,指针或引用;

        2,父类指针可以正确的指向子类对象;

        3,父类引用可以正确的代表子类对象;

        4,子类中可以重写父类中的成员函数;

  • 相关阅读:

    梯度下降法
    维特比算法
    分治法
    动态规划
    hadoop学习视频
    Java深拷贝浅拷贝
    Android NDK r8 Cygwin CDT 在window下开发环境搭建 安装配置与使用 具体图文解说
    Linux高性能server编程——定时器
    OpenGL进阶演示样例1——动态画线(虚线、实线、颜色、速度等)
  • 原文地址:https://www.cnblogs.com/dishengAndziyu/p/10915188.html
Copyright © 2011-2022 走看看