简体   繁体   English

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

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

I have a result that I didn't expect from multiple inheritance, virtual methods and pointers to base classes. 我有一个结果,我没想到多重继承, virtual方法和指向基类的指针。


With d.getStr() , when d is a derived instance, the base_2 version is called, as I expected. 使用d.getStr() ,当dderived实例时,将base_2版本,正如我所期望的那样。

With p->getStr() , when p is a pointer to a derived instance (or a pointer to base_2 pointing to a derived instance), the base_2 version is called, as I expected. 使用p->getStr() ,当p是指向derived实例的指针(或指向指向derived实例的base_2指针)时,会调用base_2版本,正如我所期望的那样。

But with p->getStr() , when p is a pointer to a base_1 pointing to a derived instance, the base_1 version is called and I was convinced would be called the base_2 version (thanks the using and the fact that getStr() are virtual methods). 但随着p->getStr()p是一个指向base_1指向derived例如, base_1版本被称为,我确信会被称为base_2版本(感谢using ,事实上, getStr()virtual方法)。

The following is a simple example: 以下是一个简单的例子:

#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;
}

The output is the following 输出如下

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

I know that, to impose the call of base_2 version, I can add in derived the following method 我知道,为了强制调用base_2版本,我可以添加derived以下方法

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

but my questions are: 但我的问题是:

1) Why does the pointer to base_1 (pointing to a derived instance) ignore the using directive and call the base_1 version of getStr() ? 1)为什么指向base_1 (指向派生实例)的指针忽略using指令并调用getStr()base_1版本?

2) Is there a way to impose the base_2 version of getStr() , when derived instance is used by a base_1 pointer, without redefining getStr() ? 2)有没有办法强加给base_2的版本getStr()derived实例使用由base_1指针,而无需再定义getStr()

--- EDIT --- ---编辑---

Thanks for the answers. 谢谢你的回答。

I understand that you are describing what's happening but my doubt is: does the language (the standard) describe this aspect? 我知道你在描述正在发生的事情,但我怀疑的是:语言(标准)是否描述了这方面的内容? Or is it an undefined part? 或者它是一个未定义的部分?

I mean: if I remove the using directive, I get a compilation error ( error: request for member getStr is ambiguous ), from d.getStr() and from dp->getStr() , because the compiler doesn't know which version of getStr() to chose. 我的意思是:如果我删除using指令,我从d.getStr()dp->getStr()得到编译错误( error: request for member getStr is ambiguous dp->getStr() ,因为编译器不知道哪个版本选择getStr()

But getStr() are virtual methods. 但是getStr()virtual方法。 So (I was convinced that) a base pointer should use the derived version of they. 所以(我确信)一个基指针应该使用它们的派生版本。 But we have a couple of colliding methods. 但我们有几种相互碰撞的方法。

From the language (standard) point of view, a base_1 (or base_2 ) is the pointer authorized (or obligated) to choose one of the two versions of the colliding methods ignoring the other? 从语言(标准)的角度来看, base_1 (或base_2 )是被授权(或有义务)选择忽略另一个的碰撞方法的两个版本之一的指针?

Maybe I'm wrong but seems to me that, in this way, the virtual methods are managed as non virtual methods. 也许我错了,但在我看来,通过这种方式, virtual方法作为非virtual方法进行管理。

You are expecting that when you using the using keyword in the following manner: 您希望以下列方式using关键字时:

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

That this is the same as: 这与以下相同:

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

In this case, the behavior you are expecting -- invoking p->getStr() , when p is a pointer to a base_1 -- would, indeed, end up invoking base_2::getStr() . 在这种情况下,您期望的行为 - 当p是指向base_1的指针时,调用p->getStr() - 实际上最终会调用base_2::getStr() derived overrides base_1 's getStr() , so invoking base_1 's getStr (), through an ordinary pointer, results in derived 's getStr () getting invoked, which invokes base_2 getStr() method. derived覆盖了base_1getStr() ,因此通过普通指针调用base_1getStr ()会导致derivedgetStr ()被调用,从而调用base_2 getStr()方法。

However, this is not what happens. 然而,这不是发生的事情。 The using keyword is not an alias for forwarding a method call in this manner. using关键字不是以这种方式转发方法调用的别名。 The using keyword does not create a method in derived 'd class, so class inheritance is not affected, and derived_1 's getStr() does not get overriden in the subclsas. using关键字不会在derived 'd类中创建方法,因此类继承不受影响,并且derived_1getStr()不会在subclsas中被覆盖。 And that's why invoking derived_1 's getStr() does not wind up invoking derived_2 's getStr() . 这就是为什么调用derived_1getStr()不会最终调用derived_2getStr()

That happens because the derived class has to have 2 vtable entries for getStr() , one for each base class, so it can correctly resolve both base_1::getStr() and base_2::getStr() . 这是因为派生类必须为getStr()提供2个vtable条目,每个基类一个,因此它可以正确解析base_1::getStr()base_2::getStr() The using directive doesn't create a derived::getStr() vtable entry or replace the base-class ones, it just selects which base-class entry will be used. using指令不创建derived::getStr() vtable条目或替换基类类,它只选择将使用哪个基类条目。 When going through a pointer to base_1 , the compiler only "sees" the vtable entries for virtual functions from derived and base_1 so it resolves getStr() to base_1::getStr() . 当通过指向base_1的指针时,编译器只从derivedbase_1 “看到”虚函数的vtable条目,因此它将getStr()解析为base_1::getStr() Your solution of adding an explicit getStr() in derived is probably the cleanest one, although it might be advisable to make it virtual to match the base classes for clarity. 您在derived中添加显式getStr()解决方案可能是最干净的解决方案,但为了清楚起见,建议将其设置为虚拟以匹配基类。

1) Seems like your code is doing exactly what it's supposed to do. 1)似乎你的代码正在完成它应该做的事情。 You point to base_1, so you get functions from base_1 (or any of its base classes). 您指向base_1,因此您从base_1(或其任何基类)获取函数。 That the object is made up of both base_1 and base_2 is unknown at that point, because you are point to a base class, not a derived class. 该对象由base_1和base_2组成,此时未知,因为您指向基类,而不是派生类。

2) No, that is simply impossible. 2)不,这根本不可能。 You must indeed overload getStr() in derived . 您必须确实在derived重载getStr()。

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

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