虚函数的返回类型应与基类或协变中的类型相同。但为什么我们有这个限制?
答案 0 :(得分:13)
由于随之而来的废话:
struct foo
{
virtual int get() const { return 0; }
};
struct bar : foo
{
std::string get() const { return "this certainly isn't an int"; }
};
int main()
{
bar b;
foo* f = &b;
int result = f->get(); // int, right? ...right?
}
让派生类返回完全不相关的东西是不明智的。
答案 1 :(得分:5)
因为使用返回值的代码如何处理各种不相关的类型? e.g:
class A
{
public:
virtual float func();
};
class B: public A
{
public:
virtual char *func();
};
A *p = (some_condition) ? new A() : new B();
p->func(); // Oh no! What is the type?
答案 2 :(得分:3)
根据C ++标准:
重写函数的返回类型应与重写函数的返回类型相同 - 与函数类相关或协变。如果函数D :: f覆盖函数B :: f,则 如果函数的返回类型满足以下条件,则它们是协变的:
1)都是指向类的引用或类的引用
2) B :: f的返回类型中的类与返回类型D :: f中的类相同,或者是 返回类型为D :: f
的类的明确且可访问的直接或间接基类3)指针或引用在D :: f的返回类型中具有相同的cv-qualification和类类型 与B :: f的返回类型中的类类型具有相同的cv资格或更少的cv资格。
答案 3 :(得分:1)
答案与Bjarne Stroustrup常见问题解答"Why can't I assign a vector<Apple*> to a vector<Fruit*>?"给出的答案非常类似。
在处理多态类型时,修改返回类型的能力会导致语言类型安全漏洞(请参阅@GManNickG的答案,了解具体示例)。
当影响返回类型时,有一种相当常见的情况是理想的:从基类型的虚方法返回多态指针时。例如,
class Base {
public:
virtual Base* parent() = 0;
};
class Child : public Base {
public:
Base* parent() override
{
return parent_;
}
private:
Parent* parent_; // Assume `Parent` type exists.
};
我们丢失了Child
了解parent_
成员的类型信息。这导致大量的铸造,即使该类型在一个定义明确的位置。我们可以使用Curiously Recurring Template Parameter(CRTP)习语
template<class ParentType>
class Base {
public:
virtual ParentType* parent()
{
return parent_;
}
private:
ParentType* parent_;
};
class Child : public Base<Parent> {
};