C ++虚函数返回类型


Answers:


86

在某些情况下,是的,只要返回类型与原始返回类型协变,派生类使用其他返回类型覆盖虚拟函数是合法的。例如,考虑以下内容:

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*并且该操作定义明确。

更一般而言,函数的返回类型从不视为其签名的一部分。您可以使用任何返回类型覆盖成员函数,只要返回类型是协变的即可。


9
此“您可以使用任何返回类型覆盖成员函数”是不正确的。只要返回类型是相同的或协变的(您已说明),您就可以覆盖。这里没有其他一般情况。
bronekk

1
@ bronekk-剩下的句子然后指出,新的返回类型必须在原始类型可以使用的任何地方都可以使用;也就是说,新类型与原始类型是协变的。
templatetypedef

句子的其余部分是不正确的;想象代替Base*longDerived*int(或其他方式,无所谓)。它不会工作。
bronekk

@ bronekk-啊,是的,我没想到!感谢您指出了这一点。
templatetypedef

1
该类型在任何原始类型都可用的地方可用,并且协方差是两个不同的上下文。协变意味着实现函数的类型之间的关系与返回的类型之间的关系以相同的方式变化逆变(尽管不是在C ++中有用)是相反的上下文。某些语言在动态分配中允许使用互变参数(如果基数采用类型T的对象,则派生类型可以采用T',其中T'是T的基数-当您沿着一个层次结构向下移动时,则向上移动另一个层次结构)。
大卫·罗德里格斯(DavidRodríguez)-dribeas 2012年

53

是。只要它们是协变的,返回类型就可以不同。C ++标准对此进行了描述(第10.3 / 5节):

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

  • 两者都是指向类或指向类98的指针
  • 返回类型B::f为的类与D::f或返回类型为的类相同,是返回类型为的类的明确的直接或间接基类,D::f并且可以在中访问D
  • 指针或引用都具有相同的cv-qualification,并且返回类型的类类型D::f具有与cv-qualification相同的cv-qualification或小于返回类型的cv-qualification B::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中的协方差和逆方差(计算机科学)


2
这是分辨率中未使用的实际功能或返回类型的副作用吗?
马丁·约克

1
@Martin,绝对是功能。我很确定过载解析与它无关。如果要覆盖函数,使用返回类型。
罗伯·肯尼迪

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.