Changing the Overridden Method’s Characteristics

修改重写方法的特征

在大多数情况下,我们重写(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。

时间: 2024-10-06 09:07:11

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

Performance - Method passes constant String of length 1 to character overridden method

This method passes a constant literal String of length 1 as a parameter to a method, that exposes a similar method that takes a char. It is simpler and more expedient to handle one character, rather than a String. Instead of making calls like: String

What Influences Method Call Performance in Java?--reference

reference from:https://www.voxxed.com/blog/2015/02/too-fast-too-megamorphic-what-influences-method-call-performance-in-java/ Whats this all about then? Let’s start with a short story. I proposed a change on the a Java core libs mailing list to overri

Java Interview Reference Guide--reference

Part 1 http://techmytalk.com/2014/01/24/java-interview-reference-guide-part-1/ Posted on January 24, 2014 by Nitin Kumar JAVA Object Oriented Concepts Java in based on Object Oriented concepts, which permits higher level of abstraction to solve any p

oracle collection

Collections Overview 一.Types of Collections 1.Associative arrays 数组      它是同种类型的一维.无边界的稀疏集合,只能用于 PL/SQL.      DECLARE TYPE t_name IS TABLE OF varchar2(10) INDEX BY PLS_INTEGER; --创建 Collection              i_name t_name;      --创建 instance           

Inheritance versus composition

One of the fundamental activities of an object-oriented design is establishing relationships between classes. Two fundamental ways to relate classes are inheritance and composition. Although the compiler and Java virtual machine (JVM) will do a lot o

最近遇到的异常与错误总结

异常 NumberFormatException  数字格式化异常 ArithmeticException 算术异常 ArrayIndexOutOfBoundsException  数组超出绑定异常:没有输入参数,或输入的参数不够 NullPointerException 空指针异常:使用了未实例化的对象 NoSuchMethodError:main  找不到主方法 ClassCastExeption:A 类转换异常 IllegalThreadStateException:非法的线程状态异常 I

【Java基础】final关键字总结

Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使用final关键字的实例.final经常和static一起使用来声明常量,你也会看到final是如何改善应用性能的. final关键字的含义? final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如

java常见异常和错误

异常 1.NumberFormatException  数字格式化异常 2.ArithmeticException 算术异常 3.ArrayIndexOutOfBoundsException  数组超出绑定异常:没有输入参数,或输入的参数不够 4.NullPointerException 空指针异常:使用了未实例化的对象 5.NoSuchMethodError:main  找不到主方法 6.ClassCastExeption:A 类转换异常 7.IllegalThreadStateExcepti

Java 中的泛型详解-Java编程思想

Java中的泛型参考了C++的模板,Java的界限是Java泛型的局限. 2.简单泛型 促成泛型出现最引人注目的一个原因就是为了创造容器类. 首先看一个只能持有单个对象的类,这个类可以明确指定其持有的对象的类型 class Holder1 { private Circle a; public Holder1(Circle a) { this.a = a; } Circle get() { return a; } } 上面的类的可重用性不怎么样,无法持有其他类型的任何对象,下面通过持有Object