我有一个带有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);
不幸的是,这不起作用。有没有正确的方法来做到这一点?或者我应该实施一个解决方案?我曾考虑让IB
和IC
几乎从A
继承,但是我上次尝试过IIRC时会出现一些令人不快的问题。
有什么想法吗?
编辑:哦,是的,这是插件API的一部分,所以很遗憾,我无法直接访问我需要T
的{{1}}类型。我的例子彼此相邻,但如上所述,它更复杂。基本上我有2个共享库。 A *
和T
(我有一个T1
)都是实现插件API的类,并且是共享库的内部。
澄清:这是我的典型插件的一个更具体的例子(它们在不同的库中):
插件A:
IB *
插件B:
class PluginA : public QObject, public PluginInterface, public OtherInterface {
};
编辑:我猜测问题是pluginA和pluginB位于不同的共享库中。也许rtti不跨越模块边界。我认为情况可能就是这样,因为人们的例子似乎在我的测试中运作良好。具体来说,如果我在其上执行“nm”,则pluginB没有“PluginA的typeinfo”。这可能是问题的核心。如果是这种情况,我只需通过虚拟继承或我的一个接口中的虚拟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.
};
函数解决它。
答案 0 :(得分: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;
}
我并不是说这是正确的解决方案,但它可能会提供一些有关正在发生的事情的信息......
答案 1 :(得分: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; }
};
答案 2 :(得分: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继承。
编辑: 我只是尝试了这个并且它有效 - 注意在编译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(); }
答案 3 :(得分:3)
我终于明白了,Daniel Paull是正确的,因为应该允许“横向dybnamic_cast
”。我的问题是因为我的代码涉及共享库。 PluginA中的typeinfo在PluginB中不可用。我的解决方案是有效地将RTLD_NOW
和RTLD_GLOBAL
添加到我的加载过程
技术上它是
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);
因为我使用的是Qt的插件系统,但差别相同。这些标志强制立即解析加载库中的所有符号,并使其他库可见。因此,将typeinfo提供给需要它的每个人。一旦这些标志到位,dynamic_cast
就会按预期工作。
答案 4 :(得分:1)
我最近也被同样的问题困扰了。有关更多信息,请参阅GCC的FAQ条目:
http://gcc.gnu.org/faq.html#dso
除了使用RTLD_ *标志指示dlopen之外,链接器也可以解决此问题的一些变化,请参阅其-E和-Bsymbolic选项。