区分Overloading、Overriding及Hiding

在面向对象(OO)的世界中存在着三个十分容易混淆的概念:重载(Overloading)、重写(Overriding)、隐藏(Hiding)。

1.重载

重载是指同一作用域的不同函数使用相同的函数名,但是函数的参数个数或类型不同。重载在C中就已经存在了,正如我们所熟悉的abs函数一样,如下所示:

double abs(double);
int abs(int);
abs(1);         // call abs(int);
abs(1.0);       // call abs(double);

重载函数就是在一个类空间里具有相同名字、不同参数的一些函数。比如下面类Maxer中的Max函数:

class Maxer {
public:

    void Max(int a, int b);

    void Max(double a, double b);

    void Max(double a, double b, double c);

    ...// other code
};

但是,如果用派生类newMaxer继承基类Maxer:

class newMaxer : public Maxer {
public:

    void Max(int a, double b);

    ...// other code
};

派生类newMaxer中的Max函数并不是基类Maxer中Max函数的重载兄弟,因为它们分属于不同的作用域。所以当写下如下代码时,编译器会报错:

newMaxer newMax;
newMax.Max(1, 3);     // 编译报错

这是因为在派生类的作用域中没有找到Max(int, int)的函数定义,基类Maxer中的Max被派生类中的Max(int, double)掩盖了,于是就出现了“参数不匹配”的错误提示。如果想让它们兄弟四个构成重载,需要将基类中的Max函数声明引入到派生类的作用域中,如下所示:

class newMaxer : public Maxer {
public:

    using Maxer::Max;
    void Max(int a, double b);

    ...// other code
};

2.重写

重写是指在派生类中对基类中的虚函数重新实现,即函数名和参数都一样,只是函数的实现体不一样。重写是我们十分熟悉的一个操作,它与虚函数的实现息息相关。这里涉及两个关键要素:派生类和虚函数,如下所示:

class Student {
public:

    Student(){}

    ~Student(){}

    virtual void Show() {
        std::cout<<"Student..."<<std::endl;
    }

};

class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}

    virtual void Show() {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

但是重写有几点必须注意:

(1)函数的重写与访问层级(public、private、protected)无关。

class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}

private:

    virtual void Show() {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

上述派生类中的Show与基类的访问层级不同,但还是成功地实现了对该函数的特殊定制。

(2)const可能会使虚成员函数的重写失效。

常量成员函数与一般的成员函数在函数签名中是不同的,其常量属性是函数签名中的一部分。

class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}

    virtual void Show()const {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

因为具有不同的函数签名,所以派生类中的Show函数并没有重写基类中的Show函数。

(3)重写函数必须和原函数具有相同的返回类型。

因为函数的返回类型不是函数签名的一部分,所以若派生类重写了基类类型中对应的函数,那么它们必须有相同的返回类型。如果返回值不同,编译器会抛出“重写虚函数返回类型有差异”的错误警示,如下所示:

class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}

    virtual bool Show() {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

该规则存在一种例外情形,称为“协变返回值类型”。协变的返回值必须是子类或是父类的指针或是引用,如下所示:

class CollegeStudent : public Student {
public:

    CollegeStudent(){}

    ~CollegeStudent(){}

    CollegeStudent& Show() {
        std::cout<<"CollegeStudent..."<<std::endl;
    }
}

需要注意的是,如果有返回值,返回值必须是子类或父类的引用或指针,如果父类的返回值是引用,那么子类返回值也是引用;如果父类返回值是指针,那么子类返回值也是指针。否则,编译将不通过。

3.隐藏

隐藏是指派生类中的函数屏蔽基类中具有相同名字的非虚函数。所以它的两个重要要素就是派生类和非虚函数。

在调用一个类的成员函数时,编译器会沿着类的继承链逐级地向上查找函数的定义,如果找到就停止。如果一个派生类和一个基类有一个同名函数,由于派生类在继承链中处于下层,编译器则最终会选择派生类中的函数。如此一来,基类的同名成员函数就会屏蔽隐藏,编译器的函数查找也就到达不了基类中。

还是采用前面的newMaxer类中的Max函数来说明这一问题,如下所示:

class Maxer {
public:

    void Max(int a, int b);

    void Max(double a, double b);

    void Max(double a, double b, double c);

    ...// other code
};

class newMaxer : public Maxer {
public:

    bool Max(int a, int b);
    void Max(int a, double b);

    ...// other code
};

当编译器在继承链中查找到Max函数时,派生类中的Max函数阻止了它向上寻找,隐藏了基类中的Max。

4.总结

最后,列出一张简单的表格让大家可以对这三者有个清晰的理解。

关系 作用域 有无virtual 函数名 参数类型 返回值类型
重载 相同 可有可无 相同 不同 可同可不同
重写(覆盖) 不同 相同 相同 相同(协变)
隐藏(重定义) 不同 可有可无 相同 可同可不同 可同可不同


个人主页:

www.codeapes.cn

原文地址:https://www.cnblogs.com/codeapes666/p/12093774.html

时间: 2024-10-07 11:45:03

区分Overloading、Overriding及Hiding的相关文章

Chapter 8. Classes

8.1. Class Declarations 8.1.1. Class Modifiers 8.1.1.1. abstract Classes 8.1.1.2. final Classes 8.1.1.3. strictfp Classes 8.1.2. Generic Classes and Type Parameters 8.1.3. Inner Classes and Enclosing Instances 8.1.4. Superclasses and Subclasses 8.1.5

c++ 面试基本知识点整理(1)

目录 2. 什么是虚拟构造函数以及析构造函数 2.1 虚函数的实质 2.2 基类的析构函数必须用虚函数 2.3 以下函数不能使用虚函数 1)普通函数 2)友元函数 3)静态成员函数 4)构造函数,拷贝函数 5)内联函数: 3. 如何定义一个抽象类 2.引入原因 3.抽象类 4.抽象类的规定 6.虚函数和纯虚函数有以下所示方面的区别 7.抽象类与接口得区别 4. 拷贝构造函数的定义 4.1 拷贝构造函数的使用 4.2 使用场景 4.3 编译原理 4.4 浅拷贝与深拷贝 5. 重载与重写的区别 ov

IN8005 Exercise Session

Exercise Session for Introductioninto Computer Science(for non Informatics studies, TUM BWL)(IN8005), WS 2019/2020Sheet 8• Homework are to be worked out independently.• Submission is done via moodle.https://www.moodle.tum.de/course/view.php?id=49786•

Java 方法重写和 Super 关键字

方法重写 在 Java 继承中,也存在着重写的概念,其实就是子类定义了和父类同名的方法. 定义:方法名称相同,返回类型相同,参数也相同.代码如下: package hello; class Father01{ public void tell(){ System.out.println("父类调用方法"); } } class Son01 extends Father01{ public void tell(){ System.out.println("子类调用方法"

JAVA笔记整理(三),JAVA中的类和方法

类 类是JAVA中一个重要的概念,可以把类理解成一个对象的抽象,这个抽象的对象包含了变量(用来描述这个对象的属性)和方法(用来描述这个对象可以干什么),类中的各个成员之间可以相互调用(static修饰的成员不能访问没有static修饰的成员). 而每个类中又必须有一个或者多个构造方法,这个构造方法用来将这个抽象的对象实例化. 类的定义格式为 [修饰符] class 类名{ 构造函数; 成员变量; 方法; } 在类中的构造函数.成员变量和方法都可以是0个或者多个 类的修饰符可以使用public.f

方法重载和方法重写的区别

区别点 重载 重写(覆写) 英文 overloading overriding 定义 方法名称相同,参数的类型或个数不同: 对权限没有要求 方法名称,参数类型,返回值类型全部相同 范围 发生在一个类中 发生在继承类中 原文地址:https://www.cnblogs.com/jasonboren/p/11052046.html

第五周学习总结&amp;第三次实验报告(String类的应用)

第五周学习总结 1.学习了继承的相关知识点: (1) 继承的格式为class 子类 extends 父类{} (2) 继承实际上是通过子类去扩展父类的功能 (3) 一个子类只能继承一个父类,也就是说,继承只允许多层继承不能多重继承 (4) 子类不能直接访问父类中的私有属性,但是可以调用父类中的getter或者setter去访问 (5) 子类对象在实例化之前必须首先调用父类中的构造方法再调用子类自己的构造方法,子类也可以直接使用super()调用父类中的无参构造 (6) 方法的覆写即子类定义了与父

Java基础-四要素之一《多态》-重写(Overriding,覆盖)-重载(Overloading)

多态性: Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义.调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法 Java的方法重写,就是各子类对父类中的方法可能有其他特殊定义,需要将父类中的方法的内容重写计算一边.方法名,返回类型,方法参数必须相同的情况下,即为重写 多态性是面向对象编程的一种特性,和方法无关,简单说,就是同样的一个方法能够根据输入数据的不同,做出不同的处理,即方法的重载——有不同的参数列表(静态多态性) 而当子

Java中Overriding和Overloading的区别

override和overload的区别   方法重载 (1)方法重载是让类以统一的方式处理不同类型数据的一种手段.多个同名函数同时存在,具有不同的参数个数/类型.重载Overloading是一个类中多态性的一种表现. (2)Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义.调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性. (3)重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相