继承的类是否有可能实现具有不同返回类型的虚拟函数(不使用模板作为返回值)?
Answers:
在某些情况下,是的,只要返回类型与原始返回类型协变,派生类使用其他返回类型覆盖虚拟函数是合法的。例如,考虑以下内容:
class Base {
public:
virtual ~Base() {}
virtual Base* clone() const = 0;
};
class Derived: public Base {
public:
virtual Derived* clone() const {
return new Derived(*this);
}
};
在这里,Base
定义了一个纯虚函数clone
,该函数返回一个Base *
。在派生的实现中,此虚拟函数使用的返回类型重写Derived *
。尽管返回类型与基本类型不同,但这是绝对安全的,因为任何时候您都可以编写
Base* ptr = /* ... */
Base* clone = ptr->clone();
对的调用clone()
将始终返回指向Base
对象的指针,因为即使返回a Derived*
,该指针也可以隐式转换为a,Base*
并且该操作定义明确。
更一般而言,函数的返回类型从不视为其签名的一部分。您可以使用任何返回类型覆盖成员函数,只要返回类型是协变的即可。
Base*
与long
和Derived*
用int
(或其他方式,无所谓)。它不会工作。
是。只要它们是协变的,返回类型就可以不同。C ++标准对此进行了描述(第10.3 / 5节):
覆盖函数的返回类型应与覆盖函数的返回类型相同或与函数的类协变。如果一个函数
D::f
重写一个functionB::f
,则如果满足以下条件,则这些函数的返回类型是协变的:
- 两者都是指向类或指向类98的指针)
- 返回类型
B::f
为的类与D::f
或返回类型为的类相同,是返回类型为的类的明确的直接或间接基类,D::f
并且可以在中访问D
- 指针或引用都具有相同的cv-qualification,并且返回类型的类类型
D::f
具有与cv-qualification相同的cv-qualification或小于返回类型的cv-qualificationB::f
。
脚注98指出“不允许使用指向类的多级指针或指向类的多级指针”。
简而言之,如果D
是的子类型B
,则in中的函数的返回类型D
需要是中的函数的返回类型的子类型B
。最常见的示例是当返回类型本身基于D
和时B
,但不一定必须如此。考虑一下,在这里我们有两个单独的类型层次结构:
struct Base { /* ... */ };
struct Derived: public Base { /* ... */ };
struct B {
virtual Base* func() { return new Base; }
virtual ~B() { }
};
struct D: public B {
Derived* func() { return new Derived; }
};
int main() {
B* b = new D;
Base* base = b->func();
delete base;
delete b;
}
之所以起作用,是因为任何的调用者func
都希望有一个Base
指针。任何Base
指针都可以。因此,如果D::func
保证总是返回一个Derived
指针,那么它将始终满足祖先类提出的约定,因为任何Derived
指针都可以隐式转换为Base
指针。因此,呼叫者将始终得到他们所期望的。
除了允许返回类型改变之外,某些语言还允许覆盖函数的参数类型改变。当他们这样做时,它们通常需要保持不变。也就是说,如果B::f
接受Derived*
,D::f
则将被允许接受Base*
。后代被允许放宽他们接受的东西,并严格要求他们返回。C ++不允许参数类型相反。如果更改参数类型,C ++会认为它是一个全新的函数,因此您开始陷入重载和隐藏状态。有关此主题的更多信息,请参见Wikipedia中的协方差和逆方差(计算机科学)。
虚拟函数的派生类实现可以具有协变返回类型。