我正在创建几个旨在提供对回调功能的访问的接口。也就是说,从接口 A继承允许类使用类型一的回调;接口 B允许类型二。从 A 和 B 继承都允许这两种类型的回调。最终目的是类 A 和 B 将通过从它们继承来处理所有脏活。
第一个问题
这是一个小例子,应该说明我遇到的一些麻烦:
class A
{
public:
static void AFoo( void* inst )
{
((A*)inst)->ABar( );
}
virtual void ABar( void ) = 0;
};
class B
{
public:
static void BFoo( void* inst )
{
((B*)inst)->BBar( );
}
virtual void BBar( void ) = 0;
};
class C : public A, public B
{
public:
void ABar( void ){ cout << "A"; };
void BBar( void ){ cout << "B"; };
};
通过拨打电话
C* c_inst = new C( );
void (*AFoo) (void*) = C::AFoo;
void (*BFoo) (void*) = C::BFoo;
AFoo( (void*)c_inst );
BFoo( (void*)c_inst );
我希望我会得到“AB”作为输出。相反,我得到“AA”。颠倒派生类的顺序(B 在 A 之前),产生“BB”。为什么是这样?
第二个问题
我使用的实际接口是模板化的,所以代码看起来更像
template <class T> class A
{
public:
static void AFoo( void* inst )
{
((T*)inst)->ABar( );
}
virtual void ABar( void ) = 0;
};
template <class T> class B
{
public:
static void BFoo( void* inst )
{
((T*)inst)->BBar( );
}
virtual void BBar( void ) = 0;
};
class C : public A<C>, public B<C>
{
public:
void ABar( void ){ cout << "A"; };
void BBar( void ){ cout << "B"; };
};
这样做的原因是 A 和 B 可以完成所有工作,但他们的实现不需要任何 C 知识。
现在,调用
C* c_inst = new C( );
void (*AFoo) (void*) = C::AFoo;
void (*BFoo) (void*) = C::BFoo;
AFoo( (void*)c_inst );
BFoo( (void*)c_inst );
产生正确的输出:“AB”。
这个小例子在这里工作得很好,但在实践中并不总是能正常工作。非常奇怪的事情开始发生,类似于上面第一个问题中的怪异。主要问题似乎是两个虚函数(或静态函数,或其他东西)并不总是能进入 C 语言。
例如,我可以成功调用 C::AFoo(),但并非总是调用 C::BFoo()。这有时取决于我从 A 和 B 派生的顺序:class C: public A<C>, public B<C>
可能会生成 AFoo 或 BFoo 都不工作的代码,而class C: public B<C>, public A<C>
可能会生成其中一个工作的代码,或者两者兼而有之。
由于类是模板化的,我可以删除 A 和 B 中的虚函数。这样做会产生工作代码,当然只要 C 中存在 ABar 和 BBar。这是可以接受的,但不是我们想要的;我宁愿知道问题是什么。
上述代码可能导致奇怪问题的原因有哪些?
为什么第二个示例会产生正确的输出,而第一个示例却没有?