繁体   English   中英

多重继承,虚方法冲突和基类指针

[英]Multiple inheritance, virtual methods collision and pointers from base classes

我有一个结果,我没想到多重继承, virtual方法和指向基类的指针。


使用d.getStr() ,当dderived实例时,将base_2版本,正如我所期望的那样。

使用p->getStr() ,当p是指向derived实例的指针(或指向指向derived实例的base_2指针)时,会调用base_2版本,正如我所期望的那样。

但随着p->getStr()p是一个指向base_1指向derived例如, base_1版本被称为,我确信会被称为base_2版本(感谢using ,事实上, getStr()virtual方法)。

以下是一个简单的例子:

#include <iostream>

struct base_1
{
   virtual std::string getStr () const
    { return "string from base 1"; }
};

struct base_2
{
   virtual std::string getStr () const
    { return "string from base 2"; }
};

struct derived : public base_1, public base_2
{ 
   using base_2::getStr;
};


int main ()
{
   derived  d;

   derived *  dp  = &d;
   base_1 *   bp1 = &d;
   base_2 *   bp2 = &d;

   std::cout << "from derived:         " << d.getStr() << std::endl;
   std::cout << "from derived pointer: " << dp->getStr() << std::endl;
   std::cout << "from base_1 pointer:  " << bp1->getStr() << std::endl;
   std::cout << "from base_2 pointer:  " << bp2->getStr() << std::endl;
}

输出如下

from derived:         string from base 2
from derived pointer: string from base 2
from base_1 pointer:  string from base 1
from base_2 pointer:  string from base 2

我知道,为了强制调用base_2版本,我可以添加derived以下方法

std::string getStr () const
 { return base_2::getStr(); }

但我的问题是:

1)为什么指向base_1 (指向派生实例)的指针忽略using指令并调用getStr()base_1版本?

2)有没有办法强加给base_2的版本getStr()derived实例使用由base_1指针,而无需再定义getStr()

---编辑---

谢谢你的回答。

我知道你在描述正在发生的事情,但我怀疑的是:语言(标准)是否描述了这方面的内容? 或者它是一个未定义的部分?

我的意思是:如果我删除using指令,我从d.getStr()dp->getStr()得到编译错误( error: request for member getStr is ambiguous dp->getStr() ,因为编译器不知道哪个版本选择getStr()

但是getStr()virtual方法。 所以(我确信)一个基指针应该使用它们的派生版本。 但我们有几种相互碰撞的方法。

从语言(标准)的角度来看, base_1 (或base_2 )是被授权(或有义务)选择忽略另一个的碰撞方法的两个版本之一的指针?

也许我错了,但在我看来,通过这种方式, virtual方法作为非virtual方法进行管理。

您希望以下列方式using关键字时:

struct derived : public base_1, public base_2
{ 
   using base_2::getStr;
};

这与以下相同:

struct derived : public base_1, public base_2
{
   void getStr()
   {
        base_2::getStr();
   }
};

在这种情况下,您期望的行为 - 当p是指向base_1的指针时,调用p->getStr() - 实际上最终会调用base_2::getStr() derived覆盖了base_1getStr() ,因此通过普通指针调用base_1getStr ()会导致derivedgetStr ()被调用,从而调用base_2 getStr()方法。

然而,这不是发生的事情。 using关键字不是以这种方式转发方法调用的别名。 using关键字不会在derived 'd类中创建方法,因此类继承不受影响,并且derived_1getStr()不会在subclsas中被覆盖。 这就是为什么调用derived_1getStr()不会最终调用derived_2getStr()

这是因为派生类必须为getStr()提供2个vtable条目,每个基类一个,因此它可以正确解析base_1::getStr()base_2::getStr() using指令不创建derived::getStr() vtable条目或替换基类类,它只选择将使用哪个基类条目。 当通过指向base_1的指针时,编译器只从derivedbase_1 “看到”虚函数的vtable条目,因此它将getStr()解析为base_1::getStr() 您在derived中添加显式getStr()解决方案可能是最干净的解决方案,但为了清楚起见,建议将其设置为虚拟以匹配基类。

1)似乎你的代码正在完成它应该做的事情。 您指向base_1,因此您从base_1(或其任何基类)获取函数。 该对象由base_1和base_2组成,此时未知,因为您指向基类,而不是派生类。

2)不,这根本不可能。 您必须确实在derived重载getStr()。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM