继承的类是否可以实现具有不同返回类型的虚函数(不使用模板作为返回)?
3 回答
在某些情况下,是的,只要返回类型与原始返回类型协变,派生类使用不同的返回类型覆盖虚函数是合法的。例如,考虑以下情况:
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*
,此指针也可以隐式转换为 aBase*
并且操作是明确定义的。
更一般地说,函数的返回类型永远不会被视为其签名的一部分。只要返回类型是协变的,您就可以使用任何返回类型覆盖成员函数。
是的。允许返回类型不同,只要它们是协变的。C++ 标准是这样描述的(§10.3/5):
覆盖函数的返回类型应与被覆盖函数的返回类型相同或与函数的类协变。如果一个函数
D::f
覆盖了一个函数B::f
,如果满足以下条件,函数的返回类型是协变的:
- 两者都是类的指针或类的引用98)
- 的返回类型中的类与或
B::f
的返回类型中的类相同,是返回类型中的类D::f
的明确直接或间接基类,D::f
并且可以在D
- 两个指针或引用都具有相同的 cv 限定,并且返回类型中的类类型具有与 的返回类型中的类类型
D::f
相同或更少的 cv 限定B::f
。
脚注 98 指出“不允许使用指向类的多级指针或对指向类的多级指针的引用”。
简而言之,如果D
是 的子类型B
,那么函数 in 的返回类型D
需要是函数 in 的返回类型的子类型B
。最常见的例子是返回类型本身基于D
and 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
接受 a Derived*
,那么D::f
将被允许接受 a Base*
。允许后代在他们将接受的东西上更宽松,在他们返回的东西上更严格。C++ 不允许参数类型逆变。如果你改变参数类型,C++ 认为它是一个全新的函数,所以你开始重载和隐藏。有关此主题的更多信息,请参阅Wikipedia 中的协变和逆变(计算机科学)。
虚函数的派生类实现可以具有协变返回类型。