区分Overloading、Overriding及Hiding
在面向对象(OO)的世界中存在着三个十分容易混淆的概念:重载(Overloading)、重写(Overriding)、隐藏(Hiding)。
重载
重载是指同一作用域的不同函数使用相同的函数名,但是函数的参数个数或类型不同。
重载在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
};
重写
重写是指在派生类中对基类中的虚函数重新实现,即函数名和参数都一样,只是函数的实现体不一样。重写是我们十分熟悉的一个操作,它与虚函数的实现息息相关。
这里涉及两个关键要素:派生类和虚函数,如下所示:
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;
}
}
需要注意的是,如果有返回值,返回值必须是子类或父类的引用或指针,如果父类的返回值是引用,那么子类返回值也是引用;如果父类返回值是指针,那么子类返回值也是指针。否则,编译将不通过。
隐藏
隐藏是指派生类中的函数屏蔽基类中具有相同名字的非虚函数。所以它的两个重要要素就是派生类和非虚函数。
在调用一个类的成员函数时,编译器会沿着类的继承链逐级地向上查找函数的定义,如果找到就停止。如果一个派生类和一个基类有一个同名函数,由于派生类在继承链中处于下层,编译器则最终会选择派生类中的函数。如此一来,基类的同名成员函数就会屏蔽隐藏,编译器的函数查找也就到达不了基类中。
还是采用前面的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。
总结
最后,列出一张简单的表格让大家可以对这三者有个清晰的理解。
关系 | 作用域 | 有无virtual | 函数名 | 参数类型 | 返回值类型 |
---|---|---|---|---|---|
重载 | 相同 | 可有可无 | 相同 | 不同 | 可同可不同 |
重写(覆盖) | 不同 | 有 | 相同 | 相同 | 相同(协变) |
隐藏(重定义) | 不同 | 可有可无 | 相同 | 可同可不同 | 可同可不同 |
个人主页:
区分Overloading、Overriding及Hiding的更多相关文章
- Override is not allowed when implementing interface method Bytecode Version Overriding and Hiding Methods
java - @Override is not allowed when implementing interface method - Stack Overflow https://stackove ...
- Polymorphism & Overloading & Overriding
In Java, a method signature is the method name and the number and type of its parameters. Return typ ...
- Java 抽象类详解
在<Java中的抽象方法和接口>中,介绍了抽象方法与接口,以及做了简单的比较. 这里我想详细探讨下抽象类. 一.抽象类的定义 被关键字“abstract”修饰的类,为抽象类.(而且,abx ...
- Object Pascal中文手册 经典教程
Object Pascal 参考手册 (Ver 0.1)ezdelphi@hotmail.com OverviewOverview(概述)Using object pascal(使用 object p ...
- 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. ...
- DELPHI学习---类和对象(五篇)
Classes and objects(类和对象) 类(或者类类型)定义了一个结构,它包括字段(也称为域).方法和属性:类的实例叫做对象:类的字段.方法和属性被称为它的部件(components)或成 ...
- IN8005 Exercise Session
Exercise Session for Introductioninto Computer Science(for non Informatics studies, TUM BWL)(IN8005) ...
- J2EE相关总结
Java Commons The Java™ Tutorials: http://docs.oracle.com/javase/tutorial/index.html Java Platform, E ...
- 每个 Java 开发者都应该知道的 5 个注解
自 JDK5 推出以来,注解已成为Java生态系统不可缺少的一部分.虽然开发者为Java框架(例如Spring的@Autowired)开发了无数的自定义注解,但编译器认可的一些注解非常重要. 在本文中 ...
随机推荐
- cocos2dx基础篇(5) 按钮
这篇是直接复制的别人的,太多了,难得写... [本节内容] CCMenu.CCMenuItem其具体的六个子类 [菜单CCMenu] 菜单CCMenu是用来装载菜单按钮的图层,图层中的子节点只能够是菜 ...
- 事务与Mysql隔离级别
事务 定义: 比如ABCD四个业务,作为一个事务,他们要么一起都执行完毕,要么都不执行.(只要有一个不成功,那么所有的都不可以成功) 四个特性 ACID 原子性(Atomicity) 整个事务中的所有 ...
- 小记---------Hadoop的MapReduce基础知识
MapReduce是一种分布式计算模型,主要用于搜索领域,解决海量数据的计算问题 MR由两个阶段组成:Map和Reduce,用户只需要实现map()和reduce()两个函数,即可实现分布式计算. 两 ...
- Vue.js官方文档学习笔记(一)起步篇
Vue.js起步 Vue.js介绍 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库 ...
- Linux 释放占用端口
在使用 Swoole 扩展库的 WebSocket 服务时,直接退出终端,但是 WebSocket 使用的端口依然坚挺的在被占用着. 导致再次启动 WebSocket 服务失败. 具体操作: 1.ne ...
- python 安装 redis
https://pypi.org/project/redis/ pip install redis import redis pool = redis.ConnectionPool( host = & ...
- sql server 角度与弧度相互转换的函数radians(x)和degrees(x)
--RANDIANS(x)将参数x由角度转换为弧度 --DEGREES(x)将参数x由弧度转换为角度. 示例:select RADIANS(90.0), RADIANS(180.0), DEGREES ...
- 使用git配置ssh的文章推荐
https://blog.51cto.com/sgk2011/1925922 https://www.cnblogs.com/superGG1990/p/6844952.html https://bl ...
- Smoke Testing
[Smoke Testing 释义] Smoke Testing 的概念最早源于制造业,用于测试管道.测试时,用鼓风机往管道里灌烟,看管壁外面是否有烟冒出来,以便检验管道是否有缝隙.这一测试显然比较初 ...
- 使用 supervisor 管理 Celery 服务
使用 supervisor 管理 Celery 服务 Celery 后台运行 如果我们想让celery worker运行在后台而不是终端上,在后台以守护进程的方式运行,我们可以使用supervisor ...