Polymorphism is not working with function return values of same data type (Base and Inherited class)
据我所知,要重写继承类中的虚函数,该函数的返回值数据类型应与基类函数相同。
但是,如果返回的指针或值属于从原始函数返回值的类继承的类,则编译器将接受更改返回值,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <iostream>
class Base{
public:
virtual Base * clone() {
std::cout <<"Base::clone()
" ;
Base * bp = new Base ;
return bp ;
}
std::string ID() {return"Base class";}
};
class Derived: public Base {
public:
//Derived* and Base* are same data type (acceptable):
Derived * clone() {
std::cout <<"Derived::clone()
" ;
Derived * dp = new Derived ;
return dp ;
}
std::string ID() {return"Derived class";}
};
int main() {
Base * bp = new Derived;
std::cout << bp->clone()->ID() <<"
";
std::cout << dynamic_cast <Derived*>(bp->clone())->ID() <<"
";
/*
next code give error: cannot convert Base* to Derived*:
Derived * dp2 = bp->clone();
std::cout << dp2->ID() <<"
";
*/
} |
G++的输出是:
1 2 3 4
| Derived::clone()
Base class
Derived::clone()
Derived class |
overriden Derived类中的clone()函数返回指向堆上同一对象副本的指针。从输出来看,每次调用正确版本的clone(),但不调用ID()。为了解决这个问题,我不得不降低返回值以获得dynamic_cast所期望的效果,或者使virtual ID()成为基类。
我的问题是:为什么多态性在第一个案例中不起作用?
1 2
| std::cout << bp->clone()->ID() <<"
"; |
因为clone()应该返回一个指向Derived类对象的指针,因此Derived类的ID()函数不是Base类,但在这种情况下,我有ID()类的Base函数?
- 你忘了在base::id前面的EDOCX1[0]了吗?目前它不是一个多态函数。
- 不,我已经按照我在问题中所说的测试了这个选项。实际上,我不打算使用虚关键字来测试id()的输出。
- 但你的问题是,为什么多态性在没有负面影响的情况下不起作用。原因是ID函数不是虚拟的。
- 如果你说bp的类型是Base,那么编译器应该如何知道bp是否是Derived?显然,一切都很清楚,因为您调用clone,就好像它是基类的方法一样。
- 我的问题是clone()将返回派生*指针,所以当我调用clone()->id时,为什么返回值被视为base*指针。所以我为什么要使用虚拟?
- 因为你是通过一个Base*呼叫ID()。
- @Galik为什么是一个Base*?协变的virtual Derived* Derived::clone()应该返回Derived*,尽管…
- 可以通过Base*或Derived*调用派生对象。要获得正确的ID(),函数需要是虚拟的,或者需要通过Derived*调用。
- @Galik,好的,但是clone()病毒属于派生的,返回派生的*指针而不是基*指针
- 它不是确定调用的返回类型,而是返回类型绑定到的指针类型。在这种情况下,这是一个Base*。
- 即使您将Derived*值绑定到它,Base* bp始终是Base*。
在这种情况下,多态性工作正常。当您预期Derived class时,代码打印Base class的原因是ID()方法不是virtual方法。
为了理解发生了什么,您必须将代码视为编译器。在您的示例中,bp是指向Derived实例的指针,但它在代码中被键入为Base *,因此编译器可以看到Base *。当代码后面的编译器看到bp->clone()时,它知道Base类的clone()方法返回Base *。最后,当编译器到达->ID()方法调用时,它会查看Base类定义并看到一个非虚拟方法,从而确保在运行时在该位置调用Base::ID()方法。
如果要具有多态行为,请为这两个id()方法添加virtual关键字。如果你使用C++ 2011兼容编译器,你也可以在EDCOX1上添加EDCOX1 16关键字17。
- 我不清楚的是:bp->clone()执行一个虚拟调度,所以它通过Base*指针调用Derived::clone(),这里没什么好奇心。但是,后者返回一个Derived*,所以bp->clone()的返回类型应该是Derived*。实际上不是,是Base*,这是为什么?
- 编译器在编译时不知道clone()调用将返回Derived*,因此它按照变量bp的定义执行。
- @确切地说,这就是我问题的意思
- @vsoftco虚拟调度意味着编译器在编译时不知道clone()函数将返回什么类型,只知道它是Base*的派生。编译器跟踪clone()返回的正确类型的唯一方法是通过虚拟调度,并且只有当ID()也声明为virtual时才会发生这种情况。
- @Galik,这是有道理的,但是引入这个时髦的协变返回类型的全部原因是什么呢?我的意思是,与简单地写Base* Derived::clone() override相比,有什么优势?
- @vsoftco我假设协变返回类型允许您在通过Derived*而不是Base*调用clone()时将Derived*绑定到Derived*变量。
- @加利克,说得对!我要弄清楚整个业务是如何工作的,我可以打赌返回指针的类型是Derived*,即使通过Base*调用…
- @加利克,这很有道理
- @用户3270926我发布了一个与你的问题非常相似的问题,stackoverflow.com/q/29179208/3093378,我认为编译器必须在编译时(而不是运行时)知道类型,因此类型推导是静态绑定的。也就是说,bp->clone()的类型是调用指针的类型,在本例中是Base*的类型,而不是bp->clone()通过虚拟调度返回的类型。
- @VSOFTCO谢谢你解释我的问题