zoukankan      html  css  js  c++  java
  • C++ 重载、重写(覆盖)、重定义(隐藏) 与 Java 重载、重写(覆盖)、隐藏的区别

    C++:
    一、重载(overload)
    指函数名相同,但是它的参数表列个数或顺序,类型不同。但是不能靠返回类型来判断。
    (1)相同的范围(在同一个作用域中) ;
    (2)函数名字相同;
    (3)参数不同;
    (4)virtual 关键字可有可无。
    (5)返回值可以不同;

    二、重写(也称为覆盖 override)
    是指派生类重新定义基类的虚函数,特征是:
    (1)不在同一个作用域(分别位于派生类与基类) ;
    (2)函数名字相同;
    (3)参数相同;
    (4)基类函数必须有 virtual 关键字,不能有 static 。
    (5)返回值相同(或是协变),否则报错;<—只要原来的返回类型是指向基类的指针或引用,新的返回类型是指向派生类的指针或引用,覆盖的方法就可以改变返回类型。这样的类型称为协变返回类型。
    (6)重写函数的访问修饰符可以不同。尽管 virtual 是 public 的,派生类中重写改写为private,protected 也是可以的

    例:

    #include <iostream>
    using namespace std;
    class A {
    public:
           virtual void fun3(int i) {
                  cout << "A::fun3() : " << i << endl;
           }
    };
    class B : public A {
    private: // 重写函数的访问修饰符可以不同 
           // 重写/覆盖
           virtual void fun3(int i) {
                  cout << "B::fun3() : " << i << endl;
           }
    };
    int main()
    {
           A a;
           B b;
           
           A * pa = &a;
           pa->fun3(3);
           pa = &b;
           pa->fun3(5);
           
           return 0;
    }
    

    输出:
    在这里插入图片描述

    三、重定义(也成隐藏)
    (1)不在同一个作用域(分别位于派生类与基类) ;
    (2)函数名字相同;
    (3)返回值可以不同;
    (4)**参数不同。**此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载以及覆盖混淆) 。
    (5)**参数相同,但是基类函数没有 virtual关键字。**此时,基类的函数被隐藏(注意别与覆盖混淆) 。
    例:

    #include <iostream>
    using namespace std;
    class A {
    public:
           virtual void fun3(double i) {
                  cout << "A::fun3() : " << i << endl;
           }
    };
    class B : public A {
    public:
           // 隐藏/重定义
           virtual void fun3(int i) {
                  cout << "B::fun3() : " << i << endl;
           }
    };
    int main()
    {
           A a;
           B b;
           b.fun3(1.68);
           
           A * pa = &a;
           pa->fun3(3);
           pa = &b;
           pa->fun3(5);
           
           return 0;
    }
    

    输出:
    在这里插入图片描述

    Java:
    重载: 相同方法名,不同签名,可以在同一个类中,也可以发生在由于继承而相关的不同类中
    例:

    public class Main {
        public static void main(String[] args) {
            // write your code here
    
            A a = new A();
            a.p(10);
            a.p(10.0);
        }
    }
    
    class B {
        public void p(double i) {
            System.out.println(i * 2);
        }
    }
    
    class A extends B {
        // 重载
        public void p(int i) {
            System.out.println(i);
        }
    }
    

    输出:

    10
    20.0
    

    重写/覆盖: 相同的签名,相同的返回值类型或者返回值类型协变(Java5.0以后才支持返回值协变),不同类中(即派生类和基类)中。
    例:

    public class Main {
        public static void main(String[] args) {
            // write your code here
    
            A a = new A();
            a.p(10);
            a.p(10.0);
        }
    }
    
    class B {
        public void p(double i) {
            System.out.println(i * 2);
        }
    }
    
    class A extends B {
        // 重写/覆盖
        public void p(double i) { // 如public int p(double i),因为返回值不同又不是协变,所以将报错!
            System.out.println(i);
        }
    }
    

    输出:

    10.0
    10.0
    

    重写/覆盖: 子类重写父类的方法,要求方法名和参数类型完全一样(参数不能是子类),返回值和异常比父类小(也叫协变,即为父类的子类)或者相同,访问修饰符比父类大或者相同。

    覆盖是对于实例方法而言的

    方法不能交叉覆盖:子类实例方法不能覆盖父类的静态方法

                  子类的静态方法也不能覆盖父类的实例方法(编译时报错)
    

    隐藏: 父类和子类拥有相同名字的属性(成员变量)或者方法( 方法隐藏只有一种形式,就是父类和子类存在相同的静态方法)时,父类的同名的属性或者方法形式上不见了,实际是还是存在的。

    隐藏是对于静态方法和成员变量(包括静态变量和实例变量) 而言的

    (1)当发生隐藏的时候,声明类型是什么类,就调用对应类的属性或者方法,而不会发生动态绑定

    (2) 属性只能被隐藏,不能被覆盖

    (3)变量可以交叉隐藏:子类实例变量/静态变量可以隐藏父类的实例/静态变量

    隐藏和覆盖的区别

    (1)被隐藏的属性,在子类被强制转换成父类后,访问的是父类中的属性

    在无强制转换时子类要访问父类的属性使用super关键字

    (2)被覆盖的方法,在子类被强制转换成父类后,调用的还是子类自身的方法

     子类要是想访问父类的方法,可以使用super关键字
    

    RTTI(run time type identification,运行时类型检查)

    RTTI只针对覆盖,不针对隐藏:因为覆盖是动态绑定,是受RTTI约束的,隐藏不受RTTI约束

    运行时类型为引用变量所指向的对象的类型,编译时类型是引用变量自身的类型。
    例:

    public class Main {
        public static void main(String[] args) {
            // write your code here
    
            Circle circle = new Circle();//本类引用指向本类对象
            Shape shape = new Circle();//父类引用指向子类对象(会有隐藏和覆盖)
    
            System.out.println(circle.name);
            circle.printType();
            circle.printName();
            //以上都是调用Circle类的方法和引用
    
            System.out.println(shape.name);//调用父类被隐藏的name属性
            shape.printType();//调用子类printType的方法
            shape.printName();//调用父类隐藏的printName方法
        }
    
    }
    
    class Shape {
        public String name = "shape";
    
        public Shape(){
            System.out.println("shape constructor");
        }
    
        public void printType() {
            System.out.println("this is shape");
        }
    
        public static void printName() {
            System.out.println("shape");
        }
    }
    
    class Circle extends Shape {
        public String name = "circle"; //父类属性被隐藏
    
        public Circle() {
            System.out.println("circle constructor");
        }
    
        //对父类实例方法的覆盖
        public void printType() {
            System.out.println("this is circle");
        }
    
        //对父类静态方法的隐藏
        public static void printName() {
            System.out.println("circle");
        }
    
    }
    

    输出:
    在这里插入图片描述

    参考文章:
    https://www.cnblogs.com/tanky_woo/archive/2012/02/08/2343203.html
    https://blog.csdn.net/snow_7/article/details/51579278

  • 相关阅读:
    前端的推荐资源
    Python 学习日志(一)
    遇到的一些elasticsearch报错信息整理
    记hyper-v导致的privoxy error(fatal error: can't bind to 127.0.0.1:1081(error number:0)),附解决方法
    Java动态代理学习笔记
    spring依赖注入中p命名空间和c命名空间区别
    python "二维" 字典(字典嵌套)
    [剑指offer] 数组中的逆序对
    [剑指offer] 复杂链表的复制
    [剑指offer] 8-10做题笔记
  • 原文地址:https://www.cnblogs.com/a3192048/p/12241282.html
Copyright © 2011-2022 走看看