我见过的协变返回类型最常见的用法是通过虚函数。我能想到的最简单的例子看起来像这样:
class Base {
public:
virtual Base& get() { return *this; }
};
class Derived : public Base {
public:
Derived& get() override { return *this; }
};
我无法真正理解的一件事是为什么要使这些功能虚拟。
get
函数的返回类型是在调用点静态定义的,即使Derived&
可以向下转换为Base::get
,我们也无法通过调用*this
来获得Derived
。这一切都意味着多态性无论如何都不适用于返回类型。对我来说,使这些函数成为非虚拟函数并让子方法重新定义基本方法看起来更合理。但人们仍然更喜欢将它们虚拟化,至少这是我注意到的。这个有解释吗?
我们先添加一个方法,让下面的例子更加清晰:
class Base {
public:
virtual Base& get() { return *this; }
};
class Derived : public Base {
public:
Derived& get() override { return *this; }
virtual void foo() { std::cout << "Hello Derived"; }
};
请注意,
foo
仅出现在Derived
中,而不出现在Base
中。
进一步考虑你有另一个层次的继承:
class Derived2 : public Derived {
public:
Derived& get() override { return *this; }
void foo() override { std::cout << "Hello Derived2"; }
};
现在您可能想要一个实现
Base
接口的实例集合和另一个实现 Derived
接口的实例集合:
std::vector<std::shared_ptr<Base>> bases;
std::vector<std::shared_ptr<Derived>> deriveds;
请注意,我使用了共享指针,因此一个实例可以包含在两个容器中。现在您可以多态地使用它们:
for (auto b : bases) b->get(); // (I) here get returns a Base&
for (auto d : deriveds) d->get().foo(); // (II) here get returns a Derived&
如果您对
Base
进行多态处理,那么 get
的返回类型不同并不重要。多态性的要点是,在(I)中你不需要关心对象的动态类型是什么,它可能是一个Base
,也可能是一个Derived
或一个Derived2
及其get
可能会返回 Base&
或 Derived&
,你不在乎。
但是,一旦你多态地使用
deriveds
,你仍然不关心实际的动态类型是什么,它可能是Derived
或Derived2
,但从Derived
的界面我们知道它是get
返回 Derived&
。
总结:在您的“最简单的示例”中,协变返回类型没有多大用处。然而,允许它的一个原因是不仅在类层次结构的顶层启用多态性,而且还在中间启用多态性,如上所示。
PS:如果将方法设置为非虚拟方法,则无法在子类中重写它。多态性仅适用于虚拟方法。