Есть у меня 3 класс (два интерфейса и один их наследник):
class IA // интерфейс 1
{
public:
virtual void FooA1() = 0;
virtual void FooA2() = 0;
};
class IB // интерфейс 2
{
public:
virtual void FooB1() = 0;
virtual void FooB2() = 0;
};
class CC: public IA, public IB // наследник интерфейсов
{
public:
// реализация интерфейсных функцийvoid FooA1() { int a1 = 1;}
void FooA2() { int a2 = 2;}
void FooB1() { int b1 = 1;}
void FooB2() { int b2 = 2;}
};
Есть функция main.
void main(void)
{
CC * pC = new CC(); // создаем экземпляр класса CCvoid * pvC = pC; // приводим его к void*
((IB*)pvC)->FooB1(); // приводим из void* к IB* и пытаемся вызвать FooB1 , но вызывается FooA1 !!!
}
Почему вызывается FooA1 вместо FooB1 ?!
Смысл извращений в том, что из своего кода я возвращаю клиентскому коду указатель на СС, но приведенный к void*. Потом клиентский код возвращает мне его в виде void*, а я привожу его к интерфейсу IB, но вызываются методы интерфейса IA. Как-будто если указатель побывал воидом, то обратной дороги нет
S> CC * pC = new CC(); // создаем экземпляр класса CC
S> void * pvC = pC; // приводим его к void*
S> ((IB*)pvC)->FooB1(); // приводим из void* к IB* и пытаемся вызвать FooB1 , но вызывается FooA1 !!!
S>
Так нельзя!!!
из void* надо возвращаться туда, откуда переходил к void*
Так вот будет работать:
((CC*)pvC)->FooB1()
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
class IObj {
public:
virtual ~IObj() = 0 {}
void* ToVoid() { return this; }
template<typename T>
static T* FromVoid( void* obj )
{ return dynamic_cast<T*>( static_cast<IObj*>( obj ) ); }
};
class CA : virtual public IObj {
// тут реализация
};
class CB : virtual public IObj {
// тут реализация
};
class CC : public CA, public CB {
// тут реализация
};
Ну и теперь пишешь себе на здоровье:
CC* pC = new CC;
void* pV = pC->ToVoid();
CB* pB = IObj::FromVoid<CB>( pV );
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
S>void main(void)
S>{
S> CC * pC = new CC(); // создаем экземпляр класса CC
S> void * pvC = pC; // приводим его к void*
S> ((IB*)(CC*)pvC)->FooB1(); // приводим из void* к IB* и пытаемся вызвать FooB1 , но вызывается FooA1 !!!
S>}
S>
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, slavo, Вы писали:
S>>
S>> CC * pC = new CC(); // создаем экземпляр класса CC
S>> void * pvC = pC; // приводим его к void*
S>> ((IB*)pvC)->FooB1(); // приводим из void* к IB* и пытаемся вызвать FooB1 , но вызывается FooA1 !!!
S>>
E>Так нельзя!!! E>из void* надо возвращаться туда, откуда переходил к void* E>Так вот будет работать:
((CC*)pvC)->FooB1()
Проблема как раз в том, что не знаю, откуда он переходил .
Re[2]: Потеря таблицы виртуальных функций
От:
Аноним
Дата:
05.09.07 14:46
Оценка:
КД>Поменяй порядок наследования КД>
КД>class CC:public IB, public IA
КД>
КД>и подумай о вечном. О бабах.
точно точно, тогда будет иметь всегда вызовы для IB.
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Здравствуйте, slavo, Вы писали:
S>>День добрый, миру конец.
КД>Как много букв и горя
S>>Почему вызывается FooA1 вместо FooB1 ?!
КД>Потому что то вызов FooB1 идет через таблицу виртуальных функций, которую ты варварским способом заимствовал у IA.
КД>Поменяй порядок наследования КД>
КД>class CC:public IB, public IA
КД>
КД>и подумай о вечном. О бабах.
А если мне надо будет тоже самое для интерфейса IA. Взад все переделывать ?
Здравствуйте, slavo, Вы писали:
S>Проблема как раз в том, что не знаю, откуда он переходил .
А ты к CA когда-то хочешь переходить? Если нет, то переходи всегда к void* через CB*.
А если ты хочешь большего, то есть регулярный выход
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, slavo, Вы писали:
S>Это хорошо, а без dynamic_cast можно?
Тогда надо знать о CC. Именно dynamic_cast инкапсулирует тут это знание...
Ещё есть вариант отказаться от виртуального наследования и выводить CC непосредстенно из IObj, а CA и CB сделать просто классами. Но вот от dynamic_cast на таком пути отказаться не получится, да и опасно...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, slavo, Вы писали:
S>>Это хорошо, а без dynamic_cast можно? E>Тогда надо знать о CC. Именно dynamic_cast инкапсулирует тут это знание... E>Ещё есть вариант отказаться от виртуального наследования и выводить CC непосредстенно из IObj, а CA и CB сделать просто классами. Но вот от dynamic_cast на таком пути отказаться не получится, да и опасно...
во, какое решение
class IReiter // вводим новый интерфейс, который будет делать кастование
{
public:
virtual void AsIA() = 0;
virtual void AsIB() = 0;
}
class IA // интерфейс 1
{
public:
virtual void FooA1() = 0;
virtual void FooA2() = 0;
};
class IB // интерфейс 2
{
public:
virtual void FooB1() = 0;
virtual void FooB2() = 0;
};
class CC: public IReinter, public IA, public IB // наследник интерфейсов
{
public:
// реализация интерфейса кастования
IA * AsIA() { return this;};
IB * AsIB() { return this;};
// реализация интерфейсных функцийvoid FooA1() { int a1 = 1;}
void FooA2() { int a2 = 2;}
void FooB1() { int b1 = 1;}
void FooB2() { int b2 = 2;}
};
void main(void)
{
CC * pC = new CC(); // создаем экземпляр класса CCvoid * pvC = pC; // приводим его к void*
IB * pIB = ((IReinter*)pvC)->AsIB(); // т.к. IReinter первый в списке наследования, то к нему приводим без проблем
pIB->FooB1(); // вызываем FooB1. Работает
}
S>class CC: public IA, public IB // наследник интерфейсов
S>{
S>public:
S> // ...
S>};
S>
S>Есть функция main.
S>
S>void main(void)
S>{
S> CC * pC = new CC(); // создаем экземпляр класса CCvoid * pvC = (void*)static_cast<IB*>(pC); // сначала приводим его к IB*, а уже потом к void*
S> ((IB*)pvC)->FooB1();
S>}
S>
Все ок: вызывающая сторона не обязана ничего знать о том, какой объект ей передали, главное, чтобы адрес виртуальной таблицы был верный.
Почитайте про COM и посмотрите реализацию IUnknown::QueryInterface в примерах — увидите то же самое.
Здравствуйте, slavo, Вы писали:
S>>>Это хорошо, а без dynamic_cast можно? S>во, какое решение
Так конечно можно, но только в случае, когда у тебя всего два интерфейса в деле IA и IB.
Если их всего два и они всегда ходят парой, то есть ещё более простое решение
[c]
struct IAB : IA, IB {};
class CC : public IAB {
// Тут реализация
};[c] И приводиться к/от void* всегда через IAB*.
А вот если у тебя интерфейсов много, и пар их тоже много бывает, то твой IReter превратииться в "dynamic_cast своими силами". ИМХО оно того не стоит.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>и подумай о вечном. О бабах.
Бабах — это имеется в виду вылет по Access Violation?
Некоторые библиотеки (MFC) нетерпимы к произвольному порядку баз. MFC-шный класс может быть только первой базой, иначе где-нибудь да получим такую фигню, которую наблюдает slavo.
Поэтому здесь, без вариантов, переделываем программу под это требование.
Если дело не в этом, то встаёт вопрос: зачем вообще было обезличивать тип объекта, превращая его в void*?
Либо — если уж обезличивать, то делать это грамотно
CC* pc = new CC;
void* pv = (void*)(IB*)pC;
.....
((IB*)pv)->FooB1(123);
Либо — идти вообще до конца и обезличить ещё и метод.
CC* pc = new CC;
boost::function<void(*)(int)> f = boost::bind(&IB::FooB1, pc, _1);
.....
f(123);
Здравствуйте, slavo, Вы писали:
КД>>Как много букв и горя
S>>>Почему вызывается FooA1 вместо FooB1 ?!
КД>>Потому что то вызов FooB1 идет через таблицу виртуальных функций, которую ты варварским способом заимствовал у IA.
КД>>Поменяй порядок наследования КД>>
КД>>class CC:public IB, public IA
КД>>
КД>>и подумай о вечном. О бабах.
S>А если мне надо будет тоже самое для интерфейса IA. Взад все переделывать ?
Можно создать два класса (один для IA, другой для IB) и использовать их в зависимости от ситуации.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>>и подумай о вечном. О бабах.
К>Бабах — это имеется в виду вылет по Access Violation?
Нет. О бабах, это значит о бабах К>Если дело не в этом, то встаёт вопрос: зачем вообще было обезличивать тип объекта, превращая его в void*?
Вот и я о том же.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --