Я создаю пару интерфейсов, предназначенных для обеспечения доступа к функциям обратного вызова. То есть наследование от интерфейса 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" в качестве вывода. Вместо этого я получаю «АА». Меняя порядок производных классов (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. Это приемлемо, но не желательно; Я бы лучше знал, в чем проблема.
Каковы возможные причины того, что приведенный выше код может вызвать странные проблемы?
Почему второй пример выдает правильный вывод, а первый - нет?