zoukankan      html  css  js  c++  java
  • C++/Java中继承关系引发的调用关系详解

    C++:

    这里引用到了 http://blog.csdn.net/haoel/article/details/1948051/ 中的内容,还请提前阅读陈大神的这篇博客后在阅读本篇。

    覆盖,实现多态的基础,通过虚函数表来实现,下面这个例子便是覆盖 Override 

     1 #include<iostream>
     2 
     3 using namespace std;
     4 
     5 class Base{
     6 public:
     7     Base(){
     8         cout << "Base::Base" << endl;
     9     }
    10 
    11     virtual void f(){
    12         cout << "Base::f" << endl;
    13     }
    14 };
    15 
    16 class Derived : public Base{
    17 public:
    18     Derived(){
    19         cout << "Devried::Derived" << endl;
    20     }
    21 
    22     void f(){
    23         cout << "Derived::f" << endl;
    24     }
    25 };
    26 
    27 int main(){
    28     Base *p = new Derived(); // Base::Base CRCL Derived::Derived
    29     p->f(); // Derived::f
    30 }

    如果 Base类f()没有被virtual修饰,如下

     1 #include<iostream>
     2 
     3 using namespace std;
     4 
     5 class Base{
     6 public:
     7     Base(){
     8         cout << "Base::Base" << endl;
     9     }
    10 
    11     void f(){
    12         cout << "Base::f" << endl;
    13     }
    14 };
    15 
    16 class Derived : public Base{
    17 public:
    18     Derived(){
    19         cout << "Devried::Derived" << endl;
    20     }
    21 
    22     void f(){
    23         cout << "Derived::f" << endl;
    24     }
    25 };
    26 
    27 int main(){
    28     Base *p = new Derived();
    29     p->f(); // Base::f
    30 }

    如果理解了陈大神博客上那些图,应该不会对这个结果产生意外,这是我画的一个草图,并不对应于真实的内存地址情况,只是为了方便说明概念,下面一排描述的是Derived:

    解析:首先p是Base类型的指针,其对于p而言其能只能按照Base类型去运算偏移地址等来访问,所以其不可能访问到Derived中的函数。也就是说父类指针的作用域只有int a , Base::f Base::g这一部分。这样就能理解了。

    隐藏,父类指针的访问域是父类部分,子类虽然拥有一份父类函数的拷贝,但如果子类中存在于父类同名的函数,则调用和变量类型一致的函数(即父类变量调用父类函数,子类变量调用子类函数,同时成对方的函数被隐藏)。例:

     1 #include<iostream>
     2 
     3 using namespace std;
     4 
     5 class Base{
     6 
     7 public:
     8     int a;
     9 
    10     Base(){
    11         cout << "Base::Base" << endl;
    12     }
    13 
    14     void f(float a){
    15         cout << "Base::f" << endl;
    16     }
    17 };
    18 
    19 class Derived : public Base{
    20 public:
    21     int d;
    22     Derived(){
    23         cout << "Devried::Derived" << endl;
    24     }
    25 
    26     void f(int a){
    27         cout << "Derived::f" << endl;
    28     }
    29 };
    30 
    31 int main(){
    32     Derived *p = new Derived();
    33     p->f(3.14f); // Derived::f
    34 }

    这里虽然传入f的参数是float型,更适合调用Base::f,但根据同名优先调用子类的原则,实际调用为Derived::f。这就是父类同名方法被隐藏,如果指针类型为父类,则称子类方法被隐藏。

    如果子类f定义为 void f() 这里对f传值就会编译不通过。

    Java

    覆盖,java的情况很简单,只要是子类含有和父类的同名方法,就是覆盖(无论子类的函数是否为abstract)。

    public class Test {
        public static void main(String[] args){
            Base base = new Derived();
            base.f(); // Derived::f
        }
    }
    
    class Base {
    
        public void f() {
            System.out.println("Base::f");
        }
    }
    
    class Derived extends Base {
        public void f() {
            System.out.println("Derived::f");
        }
    }

    隐藏,Java中也存在隐藏,不过这个隐藏和C++不太一样,子类对象引用可以调用父类的同名函数

     1 public class Test {
     2     public static void main(String[] args){
     3         Derived derived = new Derived();
     4         derived.f(3); //Derived::f
     5         derived.f(3.14f); // Base::f
     6     }
     7 }
     8 
     9 class Base {
    10 
    11     public void f(float a) {
    12         System.out.println("Base::f");
    13     }
    14 }
    15 
    16 class Derived extends Base {
    17     public void f(int a) {
    18         System.out.println("Derived::f");
    19     }
    20 }

    再讲覆盖,C++父类函数中如果调用了自己的虚成员函数,那么由于这个虚成员函数被覆盖了,所以其实相当于调用了子类的函数。例:

    #include<iostream>
    
    using namespace std;
    
    class Base{
    
    public:
        int a;
    
        Base(){
            cout << "Base::Base" << endl;
        }
    
        void f(){
            g();
        }
    
        virtual void g(){
            cout << "Base::g" << endl;
        }
    };
    
    class Derived : public Base{
    public:
        int d;
        Derived(){
            cout << "Devried::Derived" << endl;
        }
    
        void g(){
            cout << "Derived::g" << endl;
        }
    };
    
    int main(){
        Derived *p = new Derived();
        p->f(); // Derived::g
    }

    Java中类似:

     1 public class Test {
     2     public static void main(String[] args){
     3         Base base = new Derived();
     4         base.f(); //Derived::f
     5     }
     6 }
     7 
     8 class Base {
     9 
    10     public void f() {
    11         g();
    12     }
    13     
    14     public void g(){
    15         System.out.println("Base::g");
    16     }
    17 }
    18 
    19 class Derived extends Base {
    20     public void g(){
    21         System.out.println("Derived::g");
    22     }
    23 }

    由于想利用这种特性,有人会犯一个错误,在父类的构造函数中调用被覆盖的函数,由于子类在构建时先调用父类的构造函数,此时子类为构造所以不能调到子类的覆盖方法,由此

    产生了奇怪的错误。

  • 相关阅读:
    Android学习——体验活动的生命周期
    Android学习——活动的生存期
    《程序员修炼之道:从小工到专家》 阅读笔记01
    开课第七周周总结
    Android学习——活动状态
    Android学习——返回栈
    Android学习——Intent传递数据
    Android学习——隐式Activity的用法
    Android学习——隐式Activity
    Android学习——使用显示Intent
  • 原文地址:https://www.cnblogs.com/chaiwentao/p/4792567.html
Copyright © 2011-2022 走看看