一般来说,要满足覆盖关系,则传入参数、返回类型、函数名都必须一致。但是动态绑定下,基类和派生类中满足覆盖关系的虚函数却可以返回不同的类型。不过这种类型也必须满足动态绑定,即可以绑定到同一类型上。
总的来说,C++还是一个强类型的语言,唯一在类型上可有所变化的,一个是静态类型间的类型转换(隐式、显示、内置、自定义),一个就是在OOP下的动态绑定。
我之前一直以为,要想完成派生类到基类的虚函数覆盖,函数签名必须完全一致,但是我在C++ Primer5 P562上却看到了一个神奇的现象——覆盖的虚函数返回类型竟然不相同。准确的说,基类的虚函数返回类型是基类的指针,派生类的虚函数返回类型是派生类的指针。
于是我有了三种猜测:
-
虚函数覆盖,允许返回类型不同
-
虚函数覆盖,允许返回类型不同,但是返回类型间可转换(静态类型转换、动态绑定的转换)
-
虚函数覆盖,允许返回类型不同,但是返回类型间满足均可动态绑定到同一基类类型
上面三个猜测由弱变强,很简单地测试了一下:
-
令Base 与 Dirived 同一虚函数(函数名、传入参数相同)分别返回void、int
编译报错 :
错误:为‘virtual int Derived::differentRetVal()’指定了冲突的返回类型
返回类型冲突了!说明第一种失败。算是意料中。
-
令Base返回int,Derived返回float;
报相同编译错误。
这时我在想,以上还是是类型(对象)间不同(内存大小不一样),如果是指针或者引用会不会有所改善呢?
改为Base返回 int*, Derived返回 float*
结果依然报错。
看来,第二种猜测失败。只能是第三种了。
-
令Base返回 Base*, 令Derived 返回 Derived*
编译通过!
又令Base返回 Base&, 令Derived 返回 Derived&.
编译通过!
上述均可完成动态绑定,然后我试了下直接返回满足继承关系的对象。
令Base返回Base, 令Derived 返回Derived
编译报错!
综上,说明只有返回可动态绑定的类型,编译才可以被接受。
最后,我还有两个疑惑:
-
返回类型可以满足动态绑定即可,那么传入参数是否满足动态绑定也可以呢?
-
要是一个动态绑定的对象,其绑定的类型其实是基类;如果我调用该动态绑定对象中该虚函数,令虚函数返回给一个指向派生类的对象,那么该怎么办?
对第一个疑惑,很简单,直接测试,Base传入Base&, Derived传入Derived&,结果编译报错!
virtual Derived& Derived::differentRetVal(const Derived&)’ marked override, but does not override
说明不行!
第二个疑惑,其实是行不通的… 写代码测试了下,同样编译就报错了。想想,因为你是用基类去动态绑定的,然后你又想调用这个函数把返回值给一个派生类,你还不用强制类型转换,当然直接静态类型检测就错了啊!所以,第二个疑惑是自己没有想明白。