一个继承类能否使用不同的返回类型来实现虚函数(不使用模板作为返回值)?
一个继承类能否使用不同的返回类型来实现虚函数(不使用模板作为返回值)?
在某些情况下,派生类可以使用不同的返回类型覆盖虚函数,只要返回类型与原始返回类型 协变 即可合法。例如,考虑以下情况:
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
对象的指针,即使它返回Derived*
指针,该指针也会隐式转换为Base*
并且该操作是定义良好的。
更一般地,函数的返回类型从不被视为其签名的一部分。只要返回类型是协变的,就可以使用任何返回类型覆盖成员函数。
D
是B
的子类型,则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++会认为它是一个全新的函数,因此您会开始进行重载和隐藏。有关此主题的更多信息,请参见维基百科中的Covariance and contravariance (computer science)。