C++ C++;虚函数返回类型

C++ C++;虚函数返回类型,c++,inheritance,virtual-functions,overriding,return-type,C++,Inheritance,Virtual Functions,Overriding,Return Type,继承的类是否可以使用不同的返回类型实现虚拟函数(不使用模板作为返回)?是。只要返回类型是不同的,就允许它们是不同的。C++标准描述如下(第10.3或5):< /P> 重写函数的返回类型应与重写函数的返回类型相同,或与函数类共变。如果函数D::f重写函数B::f,则如果函数满足以下条件,则函数的返回类型是协变的: 两者都是指向类的指针或对类的引用98) B::f的返回类型中的类与D::f的返回类型中的类相同,或者是D::f的返回类型中的类的明确的直接或间接基类,可以在D中访问 两个指针或引用都具

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

是。只要返回类型是不同的,就允许它们是不同的。C++标准描述如下(第10.3或5):< /P> 重写函数的返回类型应与重写函数的返回类型相同,或与函数类共变。如果函数
D::f
重写函数
B::f
,则如果函数满足以下条件,则函数的返回类型是协变的:

  • 两者都是指向类的指针或对类的引用98)
  • B::f
    的返回类型中的类与
    D::f
    的返回类型中的类相同,或者是
    D::f
    的返回类型中的类的明确的直接或间接基类,可以在
    D
    中访问
  • 两个指针或引用都具有相同的cv限定,并且
    D::f
    的返回类型中的类类型的cv限定与
    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
承诺始终返回
派生的
指针,那么它将始终满足祖先类规定的契约,因为任何
派生的
指针都可以隐式转换为
指针。因此,来电者总能得到他们所期望的



除了允许返回类型变化外,某些语言还允许重写函数的参数类型变化。当他们这样做时,他们通常需要是反变的。也就是说,如果
B::f
接受
派生*
,那么
D::f
将被允许接受
基*
。后代被允许在他们接受的东西上更加宽松,在他们返回的东西上更加严格。C++不允许参数类型逆变。如果你改变了参数类型,C++认为它是一个全新的函数,所以你开始进入重载和隐藏。有关此主题的更多信息,请参阅Wikipedia。

虚拟函数的派生类实现可以具有一个。在某些情况下,是的,派生类使用不同的返回类型重写虚拟函数是合法的,只要返回类型与原始返回类型是一致的。例如,考虑以下内容:

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
对象的指针,因为即使它返回
派生的*
,该指针也会隐式转换为
Base*
,并且操作定义良好


更一般地说,函数的返回类型从未被视为其签名的一部分。只要返回类型是协变的,您就可以用任何返回类型重写成员函数。

这是一个实际的特性还是解析中未使用的返回类型的副作用?@Martin,肯定是一个特性。我很确定超负荷解决与此无关。如果要重写函数,则使用返回类型。此“您可以用任何返回类型重写成员函数”不正确。只要返回类型相同或协变(如您所解释的),就可以覆盖句点。这里没有更一般的情况了。@bronekk-您引用的句子的其余部分说明新的返回类型必须可以在原始类型所在的任何地方使用;也就是说,新类型与原始类型是协变的。句子的其余部分是不正确的;想象一下,用
long
替换
Base*
,用
int
替换
Derived*
(或者换一种方式,没关系)。“这行不通!”布朗尼克-啊,是的,我没想到!感谢您指出这一点。该类型可以在原始类型所在的任何位置使用,协方差是两种不同的上下文。协变意味着实现函数的类型之间的关系和返回的类型之间的关系以相同的方式变化。逆变(虽然在C++中没有用处)是相反的上下文。有些语言允许在动态分派中使用逆变参数(如果基类接受类型为T的对象,则派生类型可以接受类型为T的T,其中T是T的基类——当您沿着一个层次结构向下移动时,会向上移动另一个层次结构)。