一般来说,要满足覆盖关系,则传入参数、返回类型、函数名都必须一致。但是动态绑定下,基类和派生类中满足覆盖关系的虚函数却可以返回不同的类型。不过这种类型也必须满足动态绑定,即可以绑定到同一类型上。

总的来说,C++还是一个强类型的语言,唯一在类型上可有所变化的,一个是静态类型间的类型转换(隐式、显示、内置、自定义),一个就是在OOP下的动态绑定。

我之前一直以为,要想完成派生类到基类的虚函数覆盖,函数签名必须完全一致,但是我在C++ Primer5 P562上却看到了一个神奇的现象——覆盖的虚函数返回类型竟然不相同。准确的说,基类的虚函数返回类型是基类的指针,派生类的虚函数返回类型是派生类的指针。

于是我有了三种猜测:

  1. 虚函数覆盖,允许返回类型不同

  2. 虚函数覆盖,允许返回类型不同,但是返回类型间可转换(静态类型转换、动态绑定的转换)

  3. 虚函数覆盖,允许返回类型不同,但是返回类型间满足均可动态绑定到同一基类类型

上面三个猜测由弱变强,很简单地测试了一下:

  1. 令Base 与 Dirived 同一虚函数(函数名、传入参数相同)分别返回void、int

    编译报错 :

     错误:为‘virtual int Derived::differentRetVal()’指定了冲突的返回类型
    

    返回类型冲突了!说明第一种失败。算是意料中。

  2. 令Base返回int,Derived返回float;

    报相同编译错误。

    这时我在想,以上还是是类型(对象)间不同(内存大小不一样),如果是指针或者引用会不会有所改善呢?

    改为Base返回 int*, Derived返回 float*

    结果依然报错。

    看来,第二种猜测失败。只能是第三种了。

  3. 令Base返回 Base*, 令Derived 返回 Derived*

    编译通过!

    又令Base返回 Base&, 令Derived 返回 Derived&.

    编译通过!

    上述均可完成动态绑定,然后我试了下直接返回满足继承关系的对象。

    令Base返回Base, 令Derived 返回Derived

    编译报错!

    综上,说明只有返回可动态绑定的类型,编译才可以被接受。

最后,我还有两个疑惑:

  1. 返回类型可以满足动态绑定即可,那么传入参数是否满足动态绑定也可以呢?

  2. 要是一个动态绑定的对象,其绑定的类型其实是基类;如果我调用该动态绑定对象中该虚函数,令虚函数返回给一个指向派生类的对象,那么该怎么办?

对第一个疑惑,很简单,直接测试,Base传入Base&, Derived传入Derived&,结果编译报错!

virtual Derived& Derived::differentRetVal(const Derived&)’ marked override, but does not override

说明不行!

第二个疑惑,其实是行不通的… 写代码测试了下,同样编译就报错了。想想,因为你是用基类去动态绑定的,然后你又想调用这个函数把返回值给一个派生类,你还不用强制类型转换,当然直接静态类型检测就错了啊!所以,第二个疑惑是自己没有想明白。