修改重写方法的特征

在大多数情况下,我们重写(override)一个 virtual 方法是为了改变它的实现。然后,有时我们却想改变该 virtual 方法的其他的特征,这往往会带来一系列问题。

1)改变方法的返回值类型

通常,子类在重写方法时,要保持与父类一致的函数原型,方法的实现可以改变,但是原型需要保持不变。

然而,事实却并非如此。在C++中,如果父类的 virtual 方法的返回值类型是某个类(假定为类A)的指针或者引用,那么在子类中 override 该方法时,可以将其返回值改变为子类(类A的子类)的指针或者引用。这就是所谓的协变返回类型(covariant return types)。当父类与子类工作在 parallel hierarchy 中时,这个 feature 将会派上用场。注意,parallel hierarchy 是指,一个层次结构与另一个层次结构没有相交,但是存在联系。

我们来考虑一个樱桃果园模拟程序,如下图所示,就是一个parallel hierarchy ,

现在假定 Cherry Tree 类有一个 pick() 方法,这个方法的功能是从树上摘下一个樱桃:

很自然,在 BingCherryTree 子类中,我们会想 override 该 pick() 方法。由于 BingCherry 是一个 Cherry,在下面的代码中,我们可以使方法的原型保持不变。BingCherry指针将自动转换为 Cherry 指针(向上转型)。

以上的实现很好,但是,能不能更进一步呢?因为我们知道,BingCherryTree 返回的实际上是一个更为具体的BingCherry,因此,我们可以通过修改返回值类型(将 Cherry * 修改为 BingCherry *)向BingCherryTree 类的用户指明这一点,如下所示:

这里一个好的方法来判断是否可以在重写方法时改变其返回值类型:我们可以考虑在修改返回值类型后,原来已有的代码是否仍然可以良好运行。在之前的示例中,修改返回值类型没有问题,因为假定 BingCherryTree 类的 pick() 方法总是返回 Cherry * 的代码仍然可以成功编译并正常运行。道理很简单,BingCherry 是一个 Cherry,在返回 Cherry * 的地方返回 BingCherry * 总是没有问题的。

需要注意的是,我们不能在重写方法时,将返回值类型修改为完全不想关的类型,例如 void *。

小结:对于父类中的某个 virtual 方法,只要子类中某个方法与该 virtual 方法的名字以及参数列表完全相同,那么编译器即认为子类将 override 该方法,此时如果返回值类型兼容,那么没有问题,如果不兼容,编译器将会报错。

2) 修改方法的参数

如果在子类的定义中使用父类 virtual 方法的名称,但参数与父类中同名方法的参数不同,那么这不是在 override 父类的方法,而是在创建一个新方法。而父类原始的同名方法将被隐藏。

当然,我们可以使用一种较为晦涩的技术兼顾被隐藏的方法,那就是使用 using 关键字。

小结:对于非虚方法,只要重名即为隐藏。对于 virtual 方法,名字以及参数完全一样则认为子类试图 override 父类方法,如果返回值类型兼容则 override 成功,返回值类型不兼容,则报错。

3) The override keyword(C++11)

有的时候,我们本意是想 override 父类的一个 virtual 方法,但是可能一不小心,就变成隐藏父类的 virtual 方法了。来看以下例子:

我们可以通过引用来调用 Method() 方法,如下所示:

代码将正确地调用 Sub 类重写的 Method()。现在假定 override Method() 时,我们使用了int(而非double,如此一来,改变了参数类型,不再时override),如下所示:

显然,以上代码并没有 override Method() 方法,而是创建了一个新的 virtual 方法(隐藏了父类的 Method() 方法)。如果我们仍然试图像刚刚一样去调用 Method(),将会调用的是 Super 的 Method(),而非 Sub 类中定义的那个方法(多态)。

如果我们在实际的工程中,由于需求变更而修改了父类的某个 virtual 方法的函数原型,而此时却没有相应改变子类的实现。那么实际在子类中就是创建了一个新的 virtual 方法(同时,隐藏了父类的 virtual 方法),而不是 override 了父类的 virtual 方法。

此时,根据 C++11 标准,我们可以采用 override 关键字来避免这种情况,如下所示:

Sub 的定义将导致编译器报错,因为 override 关键字表明我们要重写 Super 类的 Method() 方法,但在 Super 类中的 Method() 方法参数为 double,因此在子类中并没有实现 override。

Changing the Overridden Method’s Characteristics的更多相关文章

  1. What Influences Method Call Performance in Java?--reference

    reference from:https://www.voxxed.com/blog/2015/02/too-fast-too-megamorphic-what-influences-method-c ...

  2. 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 ...

  3. CLR via C# 3rd - 08 - Methods

       Kinds of methods        Constructors      Type constructors      Overload operators      Type con ...

  4. Java Interview Reference Guide--reference

    Part 1 http://techmytalk.com/2014/01/24/java-interview-reference-guide-part-1/ Posted on January 24, ...

  5. Java Knowledge series 7

    Pepole who make a greate contribution on common libaraies deserve our respect. Component(Widget) / S ...

  6. Using default security password

    不展示Using default security password的解决办法: import org.springframework.context.annotation.Bean; import ...

  7. 关于C#你应该知道的2000件事

    原文 关于C#你应该知道的2000件事 下面列出了迄今为止你应该了解的关于C#博客的2000件事的所有帖子. 帖子总数= 1,219 大会 #11 -检查IL使用程序Ildasm.exe d #179 ...

  8. spring mvc DispatcherServlet详解之前传---FrameworkServlet

    做项目时碰到Controller不能使用aop进行拦截,从网上搜索得知:使用spring mvc 启动了两个context:applicationContext 和WebapplicationCont ...

  9. Java基础知识【上】(转载)

    http://blog.csdn.net/silentbalanceyh/article/details/4608272 (最终还是决定重新写一份Java基础相关的内容,原来因为在写这一个章节的时候没 ...

随机推荐

  1. bzoj 1862/1056 [HAOI2008]排名系统

    原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1862 很恶心的 一道题,我也不晓得自己是第几次写这题了%>_<%. 写了两种方 ...

  2. Android之Activity的四种启动模式

    当应用运行起来后就会开启一条线程,线程中会运行一个任务栈,当Activity实例创建后就会放入任务栈中.Activity启动模式的设置在AndroidManifest.xml文件中,通过配置Activ ...

  3. JavaScript高级程序设计之动态脚本及动态样式

    1.动态加载脚本(src 原理,异步,支持跨域) var loadScript = function (url, callback) { var script = document.createEle ...

  4. Java之HttpURLConnection的变态事: Keep-Alive

    HttpURLConnection的变态事: Keep-Alive JDK自带的HttpURLConnection默认启动Keep-Alive, 使用后的HttpURLConnection会放入池里重 ...

  5. Go defer延迟执行

    defer用于延迟执行,可以类比于java或c++中的析构函数. 查看一段示例代码: func Contents(filename string) (string, error) { //打开文件 f ...

  6. iOS学习之C语言函数指针

    通过函数名调用函数: int max = maxValue(4, 5); printf("max = %d\n", max);     函数类型:int (int, int) 1. ...

  7. perl DBI 学习总结(转载)

    perl DBI 学习总结 源文地址:http://blog.csdn.net/like_zhz/article/details/5441946 DBI和DBD的不同关系模型: ########### ...

  8. Qt之SQL数据库

    ---------------------------- http://blog.csdn.net/reborntercel/article/details/6991147 http://blog.c ...

  9. Microsoft Dynamics CRM 2013 安装过程图解及安装序列号

    Microsoft Dynamics CRM 2013 安装过程 图解   在安装前,先持一下SQL配置管理,将相关的服务打开.(由于在虚拟机里,许多服务需要时才会打开,像Reporting Serv ...

  10. Elasticseach部分语法总结

    索引 在Elasticsearch中,文档归属于一种类型(type),而这些类型存在于索引(index)中,我们可以画一些简单的对比图来类比传统关系型数据库 Relational DB -> D ...