【发布时间】:2011-06-07 14:21:45
【问题描述】:
继承的类是否可以实现具有不同返回类型的虚函数(不使用模板作为返回)?
【问题讨论】:
标签: c++ inheritance virtual-functions overriding return-type
继承的类是否可以实现具有不同返回类型的虚函数(不使用模板作为返回)?
【问题讨论】:
标签: c++ inheritance virtual-functions overriding return-type
在某些情况下,是的,只要返回类型与原始返回类型协变,派生类使用不同的返回类型覆盖虚函数是合法的。例如,考虑以下情况:
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*,并且操作是明确定义的。
更一般地说,函数的返回类型永远不会被视为其签名的一部分。只要返回类型是协变的,您就可以使用任何返回类型覆盖成员函数。
【讨论】:
long 替换Base* 和用int 替换Derived*(或者反过来,没关系)。它不会起作用。
是的。允许返回类型不同,只要它们是covariant。 C++ 标准是这样描述的(§10.3/5):
覆盖函数的返回类型应与被覆盖函数的返回类型相同或与函数的类协变。如果函数
D::f覆盖函数B::f,则函数的返回类型是协变的,如果满足以下条件:
- 都是类的指针或类的引用98)
B::f的返回类型中的类与D::f的返回类型中的类相同,或者,D::f的返回类型中的类的明确直接或间接基类,并且可通过D访问- 指针或引用都具有相同的 cv 限定,
D::f返回类型中的类类型具有与B::f返回类型中的类类型相同或更少的 cv 限定。
脚注 98 指出“不允许多级指向类的指针或对多级指向类的指针的引用”。
简而言之,如果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++ 认为它是一个全新的函数,所以你开始重载和隐藏。有关此主题的更多信息,请参阅 Wikipedia 中的 Covariance and contravariance (computer science)。
【讨论】:
虚函数的派生类实现可以有Covariant Return Type。
【讨论】: