繁体   English   中英

带接口的动态转换

[英]dynamic cast with interfaces

我有一个带有implements 2接口的类,并继承了1个类。 所以,通常它看起来像这样:

class T : public A, public IB, public IC {
};

代码中有一点我有IB * ,但可以真正使用A * 我希望动态演员喜欢这样:

IB *b_ptr = new T; // it's really more complicated, but serves the example
A *a_ptr = dynamic_cast<A *>(b_ptr);

不幸的是,这不起作用。 有没有正确的方法来做到这一点? 或者我应该实施一个解决方案? 我已经考虑过IBIC都是从A实际上继承A ,但是我上次尝试过IIRC时,有一些并发症让它变得不可取。

有什么想法吗?

编辑 :哦,是的,这是一个插件API的一部分,所以不幸的是我没有直接访问我需要A *T型。 我的例子彼此相邻,但如上所述,它更复杂。 基本上我有2个共享库。 TT1 (我有一个IB * )都是实现插件API并且在共享库内部的类。

澄清:这是我的典型插件的一个更具体的例子(它们在不同的库中):

插件A:

class PluginA : public QObject, public PluginInterface, public OtherInterface {
};

插件B:

class PluginB : public QObject, public PluginInterface {
    // in here, I have a PluginInterface *, but really could use a QObject *
    // unfortunately, PluginB has absolutely no knowledge of the "PluginA" type
    // it just so happens that my PluginInterface * pointer points to an object of type
    // PluginA.
};

编辑 :我猜测问题是pluginA和pluginB在不同的共享库中。 也许rtti不跨越模块边界。 我认为情况可能就是这样,因为人们的例子似乎在我的测试中运作良好。 具体来说,如果我在其上执行“nm”,则pluginB没有“PluginA的typeinfo”。 这可能是问题的核心。 如果是这种情况,我只需要通过虚拟继承或我的一个接口中的虚拟cast_to_qobject()函数来解决它。

每个类至少有一个虚拟方法吗? 如果没有,那就是你的问题。 向每个类添加虚拟析构函数应该可以解决这个问题。

以下为我愉快地工作:

class IC
{
public:
    virtual ~IC() {}
};

class IB
{
public:
    virtual ~IB() {}
};

class A
{
public:
    virtual ~A() {}
    void foo() { /* stick a breakpoint here to confirm that this is called */ }
};

class T : public A, public IB, public IC 
{
public:
    virtual ~T() {}
};


int main(void)
{
    IB *b_ptr = new T;
    A *a_ptr = dynamic_cast<A *>(b_ptr);
    a_ptr->foo();
    return 0;
}

编辑:

在所有新信息和异常行为(您的代码应该正常工作!)之后,以下是否有帮助? 我引入了一个名为IObject的接口,并使用虚拟继承来确保该基类只有一个副本。 你现在可以转向IObject然后转向A吗?

class IObject
{
public:
    virtual ~IObject() {}
};

class IC : virtual public IObject
{
public:
    virtual ~IC() {}
};

class IB : virtual public IObject
{
public:
    virtual ~IB() {}
};

class A : virtual public IObject
{
public:
    virtual ~A() {}
    void foo() { /* stick a breakpoint here to confirm that this is called */ }
};

class T : virtual public A, virtual public IB, virtual public IC
{
public:
    virtual ~T() {}
};


int main()
{
    IB *b_ptr = new T;
    A *a_ptr = dynamic_cast<A *>( dynamic_cast<IObject *>(b_ptr) );
    a_ptr->foo();
    return 0;
}

我并不是说这是正确的解决方案,但它可能会提供一些关于发生了什么的信息......

有没有正确的方法来做到这一点? 或者我应该实施一个解决方案? 我已经考虑过IB和IC都是从A实际上继承的,但是我上次尝试过IIRC时,有一些并发症让它变得不可取。

我接着说,IB和IC的定义在你的控制之下。

COM接口在Windows上的工作方式; 这些做你想做的事,即:

  • 从一个界面转换到另一个界面
  • 实现对调用者来说是不透明的
  • 只有实现知道它实现了哪些接口

这样做,你可以做类似的事情(未经测试的代码)......

interface IQueryInterface
{
  IQueryInterface* queryInterface(const Guid* interfaceId);
};

interface IB : public abstract IQueryInterface
{
  ...
};

interface IC : public abstract IQueryInterface
{
  ...
};

//within your implementation class
IQueryInterface* T::queryInterface(const Guid* interfaceId)
{
  if (matches(interfaceId,GUID_IB))
    return (IB*)this;
  if (matches(interfaceId,GUID_IC))
    return (IC*)this;
  if (matches(interfaceId,GUID_A))
    return (A*)this;
  return 0;
}

一个更简单,更硬编码的版本是:

class A; //forward reference
interface IB
{
  virtual A* castToA() { return 0; }
};
class T : public A, IB, IC
{
  virtual A* castToA() { return this; }
};

首先转到T *然后转到A:

IB *b_ptr = new T; // it's really more complicated, but serves the example
A *a_ptr = dynamic_cast<T *>(b_ptr);

如果IB一般应该可以转换为A,那么也许IB应该从A继承。

编辑:我刚试过这个并且它有效 - 注意在编译main方法时E是未知的。

struct A
{
    virtual ~A() {}
};

struct C
{
    virtual ~C() {}
};

A* GetA();

int main()
{
    C *y = dynamic_cast<C *>(GetA());
    if (y == NULL)
        cout << "Fail!";
    else
        cout << "Ok!";
}

struct E : public A, public C
{
}; 

A* GetA() { return new E(); }

我终于想通了, Daniel Paull是正确的,因为应该允许“横向dybnamic_cast ”。 我的问题是因为我的代码涉及共享库。 PluginA中的typeinfo在PluginB中不可用。 我的解决方案是有效地将RTLD_NOWRTLD_GLOBAL添加到我的加载过程中

技术上它是

loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);

因为我使用的是Qt的插件系统,但差别相同。 这些标志强制立即解析加载库中的所有符号,并使其他库可见。 因此,将typeinfo提供给需要它的每个人。 一旦这些标志到位, dynamic_cast按预期工作。

我最近也一直困扰着同样的问题。 有关更多信息,请参阅GCC的FAQ条目:

http://gcc.gnu.org/faq.html#dso

除了使用RTLD_ *标志指示dlopen之外,链接器也可以解决此问题的一些变化,请参阅其-E和-Bsymbolic选项。

暂无
暂无

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

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