我有以下问题,
假设用户使用RightParent项目,
我还需要在LeftParent中具有的其他功能,

左右父级的某些功能是纯虚拟的

传递给用户的实际项目继承自rightParent(原始合同)和leftParent(我在此处添加的合同)

它可能看起来像这样(可编译,并且在VS 2010中也是如此)

左父母-新增合同/功能(即我拥有该课程)

#pragma once
struct CParent_Left
{
   CParent_Left(){}
   virtual void Foo() const =0;
   virtual ~CParent_Left(){}
};


正确的父母-原始合同(实际上是Qt对象-即我不拥有该类)

#pragma once
struct CParent_Right
{
   CParent_Right(){}
   virtual void Bar() const =0;
   virtual ~CParent_Right(){}
};


项(存在多个项,并且程序中使用的所有项均从父母双方继承)

#pragma once

#include "CParent_Left.h"
#include "CParent_Right.h"

class CItem : public CParent_Left, public CParent_Right
{
   public:
      CItem(){}
      virtual void Foo() const override {}
      virtual void Bar() const override {}
      virtual ~CItem(){}
};


用户(通常是从Qt QObject继承的用户)本身(和Qt私有内幕)将项目视为rightParent,此处实现的附加功能有时需要将项目视为leftParent

#pragma once

#include "CParent_Left.h"
struct CParent_Right;

class CUser
{
   CParent_Right * data; // normally obtained from parent Qt class

   public:
      CUser(CParent_Right * data) : data(data){}

      void CallFoo()
      {
         ((CParent_Left*)data)->Foo(); // calls Bar()
      }

      virtual ~CUser(){}
};


样本主

#include "CItem.h"
#include "CUser.h"

void main(int argc, char *argv[])
{
   auto item(new CItem());
   item->Foo(); // calls foo

   auto user(new CUser(item));
   user->CallFoo();
}


问题是,
当我拿着该项目并从左父项或右父项调用方法时,它们会被正确调用,

当我将项目保持为rightParent的指针(可以从Qt小部件获得)时,我知道它是从这两者继承的一些CItem(CItem1,CITem2 ...)(因此CItem为CLeft_Parent,CItem为CRight_Parent ),当我尝试将其强制转换为leftParent时,对leftParent方法的调用无法正常工作(在调试器中,跳转到vftable的位置好像已损坏)

有任何想法吗?

最佳答案

多重继承确实非常强大。

但是,使用铸件时需要格外小心。不幸的是,您所做的是不确定的行为。

这是怎么了?


首先,通过向构造函数传递CUser指针来创建CItem
由于仅为CParent_Right定义了构造函数,因此编译器会将CItem强制转换为CParent_Right
从这一刻开始,传递给构造函数的指针就是CParent_Right子对象的指针。您不能再假定它是CItem了;并非所有CParent_Right子对象都一定是CItems
因此,强制转换((CParent_Left*)data)->Foo();不会像您期望的那样产生指向CParent_Left的指针。它仅采用CParent_Right的当前地址,并将其用作CParent_Left所在的地址。这是未定义的行为。对于您的编译器,它只采用vtable中第一个函数的地址,这会导致这种不匹配。


如何纠正呢?

如果确定构造函数的CParent_Right实际上是CItem,则允许您向下转换到CItem。这是合法的并且可以正确完成。然后,您可以将CItem转换为CParent_Left,它将按预期工作:

    // ((CParent_Left*)data)->Foo(); // calls Bar() ===>  OUCH !!!!
    static_cast<CParent_Left*>(static_cast<CItem*>(data))->Foo(); // YES !!


请注意,我在这里使用static_cast是因为我确定我的对象类型,并且即使对于非多态类也可以使用这种构造。但是,如果有疑问,dynamic_cast将是一个更安全的选择。

对象布局和投射

一张小图来说明可能的转换:

关于c++ - parent __vftable之间的多重继承转换似乎已损坏,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31090190/

10-11 18:23