7

我有一个实现 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,但上次我尝试 IIRC 时,有一些并发症使其不受欢迎。

有什么想法吗?

编辑:哦,是的,这是插件 API 的一部分,所以不幸的是我无法直接访问T我需要的类型A *。我的示例彼此相邻,但如前所述,它更复杂。基本上我有 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 的类型信息”。这可能是问题的核心。如果是这种情况,我只需通过虚拟继承或cast_to_qobject()我的一个接口中的虚拟函数来解决它。

4

5 回答 5

7

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

以下很高兴为我工作:

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

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

于 2009-01-14T05:37:54.100 回答
5

有没有合适的方法来做到这一点?还是我应该实施解决方法?我曾考虑过让 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; }
};
于 2009-01-14T05:41:45.573 回答
4

我终于明白了,丹尼尔·保尔是正确的,dybnamic_cast应该允许“横向”。我的问题是因为我的代码涉及共享库。PluginA 中的 typeinfo 在 PluginB 中不可用。我的解决方案是有效地添加RTLD_NOW并添加RTLD_GLOBAL到我的加载过程中

从技术上讲,它是

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

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

于 2009-01-24T02:51:23.950 回答
3

先转换为 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 继承。

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

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(); }
于 2009-01-14T05:16:53.190 回答
1

我最近也被同样的问题困扰。有关详细信息,请参阅 GCC 的常见问题解答条目:

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

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

于 2009-07-20T13:54:44.183 回答