C++中重载、覆盖与隐藏的区别(转)
本文摘自林锐博士的《高质量C++/C编程指南》。
成员函数的重载、覆盖(override)与隐藏很容易混淆,C++程序员必须要搞清楚概念,否则错误将防不胜防。
1.重载与覆盖
成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual关键字。
在下例中,函数Base::f(int)与Base::f(float)相互重载,而Base::g(void)被Derived::g(void)覆盖。
#include <iostream> class Base
{
public:
void f(int x){ cout << "Base::f(int) " << x << endl; }
void f(float x){ cout << "Base::f(float) " << x << endl; }
virtual void g(void){ cout << "Base::g(void)" << endl; }
}; class Derived : public Base
{
public:
virtual void g(void){ cout << "Derived::g(void)" << endl; }
}; void main(void)
{
Derived d;
Base *pb = &d;
pb->f(); // Base::f(int) 42
pb->f(3.14f); // Base::f(float) 3.14
pb->g(); // Derived::g(void)
}
2.令人迷惑的隐藏规则
本来仅仅区别重载与覆盖并不算困难,但是C++的隐藏规则使问题复杂性陡然增加。这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
示例程序A中:
(1)函数Derived::f(float)覆盖了Base::f(float)。
(2)函数Derived::g(int)隐藏了Base::g(float),而不是重载。
(3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。
// 示例A #include <iostream> class Base
{
public:
virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
void g(float x){ cout << "Base::g(float) " << x << endl; }
void h(float x){ cout << "Base::h(float) " << x << endl; }
}; class Derived : public Base
{
public:
virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
void g(int x){ cout << "Derived::g(int) " << x << endl; }
void h(float x){ cout << "Derived::h(float) " << x << endl; }
};
据作者考察,很多C++程序员没有意识到有“隐藏”这回事。由于认识不够深刻,“隐藏”的发生可谓神出鬼没,常常产生令人迷惑的结果。
示例B中,bp和dp指向同一地址,按理说运行结果应该是相同的,可事实并非这样。
// 示例B void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d; // Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14 // Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3 (surprise!) // Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}
3.摆脱隐藏
隐藏规则引起了不少麻烦。示例8-2-3程序中,语句pd->f(10)的本意是想调用函数Base::f(int),但是Base::f(int)不幸被Derived::f(char *)隐藏了。由于数字10不能被隐式地转化为字符串,所以在编译时出错。
class Base
{
public:
void f(int x);
}; class Derived : public Base
{
public:
void f(char *str);
}; void Test(void)
{
Derived *pd = new Derived;
pd->f(); // error
}
从以上示例看来,隐藏规则似乎很愚蠢。但是隐藏规则至少有两个存在的理由:
- 写语句pd->f(10)的人可能真的想调用Derived::f(char *)函数,只是他误将参数写错了。有了隐藏规则,编译器就可以明确指出错误,这未必不是好事。否则,编译器会静悄悄地将错就错,程序员将很难发现这个错误,流下祸根。
- 假如类Derived有多个基类(多重继承),有时搞不清楚哪些基类定义了函数f。如果没有隐藏规则,那么pd->f(10)可能会调用一个出乎意料的基类函数f。尽管隐藏规则看起来不怎么有道理,但它的确能消灭这些意外。
在以上示例中,如果语句pd->f(10)一定要调用函数Base::f(int),那么将类Derived修改为如下即可。
class Derived : public Base
{
public:
void f(char *str);
void f(int x) { Base::f(x); }
};
C++中重载、覆盖与隐藏的区别(转)的更多相关文章
- C++中重载、覆盖和隐藏的区别,以及适用场景
一.重载.覆盖和隐藏的区别 二.适用场景 1.重载: 适用于不同的数据类型都需要使用到的功能函数.以数据相加的函数为例,可以在同一个文件内提供以下的重载函数以支持同样的功能: int add(int, ...
- java的重载、覆盖和隐藏的区别
重载:方法名相同,但参数不同的多个同名函数 注意:1.参数不同的意思是参数类型.参数个数.参数顺序至少有一个不同 2.返回值和异常以及访问修饰符,不能作为重载的条件(因为对于匿名调用,会出现歧义,eg ...
- ZT C++ 重载、覆盖和隐藏的区别
重载.覆盖和隐藏的区别 分类: C++ 学习笔记 学习心得与方法 2013-09-26 11:21 50人阅读 评论(0) 收藏 举报 概念区分 “overload”翻译过来就是:超载,过载,重载,超 ...
- Java_类和对象(完美总结)_转载_覆盖和隐藏的区别,覆盖就不能使用了,而隐藏提供全局方法名或者全局变量名还可以使用
转载自海子:http://www.cnblogs.com/dolphin0520/p/3803432.html Java:类与继承 对于面向对象的程序设计语言来说,类毫无疑问是其最重要的基础.抽象.封 ...
- C++中的覆盖与隐藏(详细讲解)
C++类中覆盖与隐藏一直是一个容易理解出错的地方,接下来我就详细讲解一下区别在何处 覆盖指的是子类覆盖父类函数(被覆盖),特征是: 1.分别位于子类和父类中 2.函数名字与参数都相同 3.父类的函数是 ...
- JAVA中方法和变量在继承中的覆盖和隐藏
出处:http://renyanwei.iteye.com/blog/258304 我们知道,在JAVA中,子类可以继承父类,如果子类声明的方法与父类有重名的情况怎么办,大伙儿都知道要是重写,但是实际 ...
- JAVA中方法和变量在继承中的覆盖和隐藏(一)
我们知道,在JAVA中,子类可以继承父类,如果子类声明的方法与父类有重名的情况怎么办,大伙儿都知道要是重写,但是实际上这又分为两种情况,就是方法和变量在继承时的覆盖和隐藏问题,这些概念性的东西看似无聊 ...
- makefile中重载与取消隐藏规则示例
学习<跟我一起写Makefile-陈皓>后一直不懂,如何重载或取消隐藏规则 为了博客版面整洁,何为隐藏规则,Makefile基本规则编写等基础支持请自行百度. 需要声明的是:这些知识可能在 ...
- Java中的覆盖和隐藏以及final关键字
Java覆盖和隐藏 (1) 变量只能被隐藏(包括静态和非静态),不能被覆盖: (2) 可以用子类的静态变量隐藏父类的静态变量,也可以用子类的非静态变量隐藏父类的静态变量,也可以用非最终变量(final ...
随机推荐
- 一个貌似比较吊的递归转换为loop--总算成功了.--第二弹
前段时间用类似于散弹式编程的方式,各种猜测-运行验证-修正结果,最终成功转换了一个看起来比较有难度的递归函数.但总觉得很蛋疼,原因如下: 1.虽然正确,但是逻辑搞得比较复杂.现在去看,一头雾水,不知道 ...
- JVM垃圾回收总结
来自Oracle官方文档,对JVM GC知识整理的清晰易懂,查资料还是看官方的好! 1 GC步骤简述 步骤1:标记 (Marking) 根据对象引用关系,将未被任何对象引用的对象实例标记出来,如下图中 ...
- android的activity栈管理
在进行BlackBerry程序开发的时候,BlackBerry提供了一个管理Screen的栈,用来从任何地方来关闭位于最上一层的Screen,使用UiApplication.getUiApplicat ...
- 自制Linux重命名命令
相比于Windows上的ren命名,Linux还真的是没有一个特定的重命名的命令.(虽然可以间接的使用mv来实现).下面我就来自己写一个简单的重命名命令. 准备工作 操作系统: Linux内核的系统都 ...
- iOS9中如何注册远程通知
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 在以往的版本中,我们可以通过: [[UIApplication ...
- Maven 介绍、安装使用
简介 Maven是一个强大的构建工具,能够帮我们自动化构建过程,从清理.编译.测试到生成报告,再到打包和部署.只要使用Maven配置好项目,然后执行命令(如mvn clean inst ...
- 自己写一个网页版的Markdown实时编辑器
这几天忙着使用Python+Django+sqlite 搭建自己的博客系统,但是单纯的使用H5的TextArea,简直太挫了有木有.所以,就想模仿一下人家内嵌到网页上的Markdown编辑器,从而让自 ...
- antlr v4 使用指南连载4——词法规则入门之黄金定律
词法规则入门 黄金定律一二 若输入串能被多个词法规则匹配,那么声明在词法文件最前面的规则生效. parser parser grammar HelloParser; options { languag ...
- java内存垃圾回收模型
一.java的内存模型 介绍如下6个组成部分 1.程序计数器:一块较小内存区域,指向当前所执行的字节码.如果线程正在执行一个Java方法,这个计数器记录正在执行的虚拟机字节码指令的地址,如果执行的是N ...
- tomcat如何路由映射网址
对于web容器来说,根据请求客户端路径路由到对应的资源属于其核心功能,假设用户在自己电脑上使用浏览器输入网址http://www.test.com/test/index.jsp,报文通过互联网网络到达 ...