C++ 虚函数返回类型

C++ virtual function return type

提问人:zzzbbx 提问时间:1/12/2011 最后编辑:templatetypedefzzzbbx 更新时间:4/20/2021 访问量:65390

问:

继承的类是否可以实现具有不同返回类型的虚函数(不使用模板作为返回值)?

C++(英语:C++) 遗产 虚拟函数 重写 返回类型

评论


答:

56赞 Rob Kennedy 1/12/2011 #1

是的。只要返回类型是协变的,就允许它们不同。C++ 标准是这样描述的(§10.3/5):

重写函数的返回类型应与被重写函数的返回类型相同,或与函数的类协变。如果一个函数覆盖一个函数,则函数的返回类型是协变的,如果满足以下条件:D::fB::f

  • 两者都是指向类的指针或对类98 的引用)
  • 返回类型 的类与返回类型 的 or 中的类是同一个类,是返回类型 的类的明确直接或间接基类,可在B::fD::fD::fD
  • 两个指针或引用具有相同的 cv 限定,并且返回类型 中的类类型与返回类型 中的类类型具有相同的 cv 限定,或比返回类型中的类类型具有相同的 cv 限定。D::fB::f

脚注 98 指出,“不允许使用指向类的多级指针或指向类的多级指针。

简而言之,如果 是 的子类型,则 中的函数的返回类型必须是 中函数的返回类型的子类型。最常见的例子是返回类型本身基于 和 ,但不必如此。考虑一下,我们有两个单独的类型层次结构:DBDBDB

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;
}

这样做的原因是,任何调用者都期望指针。任何指针都可以。因此,如果承诺始终返回指针,那么它将始终满足祖先类制定的约定,因为任何指针都可以隐式转换为指针。因此,来电者将始终得到他们所期望的。funcBaseBaseD::funcDerivedDerivedBase


除了允许返回类型发生变化外,某些语言还允许重写函数的参数类型发生变化。当他们这样做时,他们通常需要逆变。也就是说,如果接受 a ,则允许接受 .后代被允许在他们接受的东西上更宽松,在他们返回的东西上更严格。C++ 不允许参数类型的逆变。如果更改参数类型,C++ 会将其视为一个全新的函数,因此您开始进入重载和隐藏状态。有关此主题的更多信息,请参阅维基百科中的协方差和逆方差(计算机科学)。B::fDerived*D::fBase*

评论

2赞 Martin York 1/12/2011
这是实际特征还是未在分辨率中使用的返回类型的副作用?
3赞 Nikolai Fetissov 1/12/2011 #2

虚函数的派生类实现可以具有协变返回类型

97赞 templatetypedef 1/12/2011 #3

在某些情况下,是的,只要返回类型与原始返回类型协变,派生类使用不同的返回类型覆盖虚函数是合法的。例如,请考虑以下事项:

class Base {
public:
    virtual ~Base() {}

    virtual Base* clone() const = 0;
};

class Derived: public Base {
public:
    virtual Derived* clone() const {
        return new Derived(*this);
    }
};

这里,定义了一个名为 的纯虚函数,该函数返回 .在派生实现中,使用返回类型 重写此虚函数。尽管返回类型与基数中的返回类型不同,但这是完全安全的,因为任何时候您都会写入BasecloneBase *Derived *

Base* ptr = /* ... */
Base* clone = ptr->clone();

对的调用将始终返回指向对象的指针,因为即使它返回 ,此指针也可以隐式转换为 a,并且操作是明确定义的。clone()BaseDerived*Base*

更一般地说,函数的返回类型永远不会被视为其签名的一部分。您可以使用任何返回类型覆盖成员函数,只要返回类型是协变的。