Тема конечно названа неудачно, надеюсь код просянит ситуацию:
class Base
{
public:
virtual Base* getSelf()
{return this;}
};
class Derived1 : public Base
{
public :
virtual Derived1* getSelf()
{return this;};
};
class Derived2 : public Base
{
public :
virtual Derived2* getSelf()
{return this;};
};
class Tester
{
public:
virtual void Test(Derived1* Value)
{
::MessageBox(NULL, _T("TEST Derived1"),_T("TEST Derived1"), MB_OK);
}
virtual void Test(Derived2* Value)
{
::MessageBox(NULL, _T("TEST Derived2"),_T("TEST Derived2"), MB_OK);
}
};
...
//хочется что бы в точке использования этого хозяйства
Base* pBase1 = new Derived1;
Base* pBase2 = new Derived2;
Tester* pTester = new Tester;
pTester->Test(pBase1->getSelf());// вот здесь хочу вызов правильного Test'a а не ошибку компиляции
Если тема уже поднималась, то просто ткните меня носом в поиск. Спасибо!
Re: Динамический полиморфизм приперегрузке функций
Методы getSelf в классах-потомках отличаются от метода getSelf класса Base возвращаемым типом — и, соответственно, не перегружают его, а скрывают (о чем компилятор наверняка пишет warning). Классы Derived1 и Derived2 не имеют перегруженного метода Base* getSelf(), и наследуют его от базового класса. Таким образом, в строчке, где возникает ошибка, всегда вызывается метод Base::getSelf.
Сделать то, что хочется, можно так (это, фактически, паттерн Visitor):
class Base
{
public:
virtual void testMe(Tester* t) = 0;
};
class Derived1 : public Base
{
public :
virtual void testMe(Tester* t)
{ t->Test(this);}
};
class Derived2 : public Base
{
public :
virtual void testMe(Tester* t)
{ t->Test(this);}
};
Re[2]: Динамический полиморфизм приперегрузке функций
Здравствуйте, Аноним, Вы писали:
А>Методы getSelf в классах-потомках отличаются от метода getSelf класса Base возвращаемым типом — и, соответственно, не перегружают его, а скрывают (о чем компилятор наверняка пишет warning). Классы Derived1 и Derived2 не имеют перегруженного метода Base* getSelf(), и наследуют его от базового класса. Таким образом, в строчке, где возникает ошибка, всегда вызывается метод Base::getSelf.
Извините, но вы не правы. Это называется ковариантными типами возращаемых значений. С++ это поддерживает. В этом месте проблеммы нет.
(Компилятор ворнинга не выдает )
Re: Динамический полиморфизм приперегрузке функций
Здравствуйте, dip_2000, Вы писали:
_>Тема конечно названа неудачно, надеюсь код просянит ситуацию: _>... _>//хочется что бы в точке использования этого хозяйства _>Base* pBase1 = new Derived1; _>Base* pBase2 = new Derived2;
_>Tester* pTester = new Tester; _>pTester->Test(pBase1->getSelf());// вот здесь хочу вызов правильного Test'a а не ошибку компиляции _>[/ccode]
1) Добавить в Tester следующий метод:
virtual void Test(Base* Value) {...}
2) Добавить метод Test() для каждого класса в иерархии от Base.
Второй вариант предпочтительней так как для разных классов можно использовать один метод Test().
Re[2]: Динамический полиморфизм приперегрузке функций
Простите, а что это даст ? Мне нужно что бы метод вызывался для потомков, а не для абстрактного(в общем случае) предка
N>2) Добавить метод Test() для каждого класса в иерархии от Base.
В моем примере сделано не так ?
Re[3]: Динамический полиморфизм приперегрузке функций
От:
Аноним
Дата:
16.07.07 13:32
Оценка:
_>Извините, но вы не правы. Это называется ковариантными типами возращаемых значений. С++ это поддерживает. В этом месте проблеммы нет. _>(Компилятор ворнинга не выдает )
Да, в самом деле, getSelf для потомков перегружают Base::getSelf. Не знал про такую фичу
Но все равно, вы хотите, чтобы компилятор сделал выброр между функциями Tester::Test(Derived1*) и Tester::Test(Derived2*) на основании типа, который вернет функция getSelf, а этот тип будет известен только во время выполнения. В данном случае, видимо, значение, возвращаемое pBase1->getSelf(), неявно статически приводится к Base*, а потом компилятор не может привести Base* к Derived1* или Derived2*, поэтому и сообщает об ошибке.
Re[4]: Динамический полиморфизм приперегрузке функций
А>Да, в самом деле, getSelf для потомков перегружают Base::getSelf. Не знал про такую фичу А>Но все равно, вы хотите, чтобы компилятор сделал выброр между функциями Tester::Test(Derived1*) и Tester::Test(Derived2*) на основании типа, который вернет функция getSelf, а этот тип будет известен только во время выполнения. В данном случае, видимо, значение, возвращаемое pBase1->getSelf(), неявно статически приводится к Base*, а потом компилятор не может привести Base* к Derived1* или Derived2*, поэтому и сообщает об ошибке.
Не правда! В случае когда есть одна функция (например
void Test(Derived1* Value)
) и в нее передаем Derived1 — все корректно.
Я понимаю почему возникает проблемма при компиляции, я спрашиваю есть ли хороший способ это обойти? Без switch и RTTI(для того что бы от них уйти и придумана такая иерархия)?
Еще раз: совершенно очевидно, что при статическом полиморфизме(перегрузке функций) у нас проблемм бы не возникло. Но, как верно замечено, мы узнаем тип только в момент исполнения. Но. Он нам известен. Известен конкретный тип. Как можно для него вызвать нужную функцию?
Re[3]: Динамический полиморфизм приперегрузке функций
Здравствуйте, dip_2000, Вы писали:
N>>1) Добавить в Tester следующий метод: N>>
N>>virtual void Test(Base* Value) {...}
N>>
_>Простите, а что это даст ? Мне нужно что бы метод вызывался для потомков, а не для абстрактного(в общем случае) предка
Ну так этот метод и не будет вызываться для Base если он абстрактный, более того теперь можно спокойно скомпилировать код.
Для самоуспокоения можешь сделать так:
virtual void Test(Base* Value) {assert(false);}
N>>2) Добавить метод Test() для каждого класса в иерархии от Base. _>В моем примере сделано не так ?
Я имел ввиду следующее:
class Base
{
public:
virtual Base* getSelf()
{return this;}
virtual void Test() {/*общий код*/};
};
class Derived1 : public Base
{
public :
virtual Derived1* getSelf()
{return this;};
virtual void Test() {/*специфика для Derived1*/}
};
class Derived2 : public Base
{
public :
virtual Derived1* getSelf()
{return this;};
//а тут например, можно не писать свой Test(), будет использоваться Base::Test()
};
Возможно в этом случае надобность в классе Tester отпадет.
Re: Динамический полиморфизм приперегрузке функций
Здравствуйте, dip_2000, Вы писали:
_>Тема конечно названа неудачно, надеюсь код просянит ситуацию:
_>...
CRTP ?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Динамический полиморфизм приперегрузке функций
От:
Аноним
Дата:
16.07.07 14:07
Оценка:
_>Я понимаю почему возникает проблемма при компиляции, я спрашиваю есть ли хороший способ это обойти? Без switch и RTTI(для того что бы от них уйти и придумана такая иерархия)?
Здравствуйте, dip_2000, Вы писали:
_>Еще раз: совершенно очевидно, что при статическом полиморфизме(перегрузке функций) у нас проблемм бы не возникло. Но, как верно замечено, мы узнаем тип только в момент исполнения. Но. Он нам известен. Известен конкретный тип. Как можно для него вызвать нужную функцию?
Мне кажется, что паттер "посетитель", который в самом начале предложил Аноним 93, очень подходит к данной проблеме. В нем, как раз и решается проблема статического типа. Был указатель на базовый объект. Теперь указатель this.
Re: Динамический полиморфизм приперегрузке функций
Здравствуйте, dip_2000, Вы писали:
_>Тема конечно названа неудачно, надеюсь код просянит ситуацию:
Возможно задача такая — имеется допустим по 10 классов в двух иерархиях. Как изобразить специиализации для некоторых из возможных 100 пар? Причем динамически (?).
Re[2]: Динамический полиморфизм приперегрузке функций
Здравствуйте, dip_2000, Вы писали:
_>Я понимаю почему возникает проблемма при компиляции, я спрашиваю есть ли хороший способ это обойти? Без switch и RTTI(для того что бы от них уйти и придумана такая иерархия)?
По-моему, типичная ситуация для паттерна Visitor.
Re: Динамический полиморфизм приперегрузке функций
Спасибо Всем большое за ответы! Попробую ответить всем сразу :
Итак задача примерно такова классы из иерархии Base(конкретные экзампляеры Derived) передаются другому процессу при помощи WM_COPYDATA(способ не важен). В другом процессе у меня есть указатель void* который я безболезненно могу преобразовать к Base. Дальше у меня остается 2 варианта: использовать некий признак типа и строить большущий switch(и вызывать функции ProcessDerived1(), ProcessDerived2()) либо сделать, что то наподобии того что я описал. При этом класс Tester — класс в принимающем процессе, и функции нужно было назвать ProcessMess()
Паттерн Visitor не хотелось бы использовать из соображений инкапсуляции. Не хорошо, когда код клиента знает, что и как устроенно на сервере. Классы из иерархии Base — просто сообщения. Они не должны знать устройство их обработчика!
Re[4]: Динамический полиморфизм приперегрузке функций
_>>Простите, а что это даст ? Мне нужно что бы метод вызывался для потомков, а не для абстрактного(в общем случае) предка N>Ну так этот метод и не будет вызываться для Base если он абстрактный, более того теперь можно спокойно скомпилировать код. N>Для самоуспокоения можешь сделать так: N>
Разумеется начал вызываться void Test (Base* Value)
N>Я имел ввиду следующее: N>
N>class Base
N>{
N>public:
N> virtual Base* getSelf()
N> {return this;}
N> virtual void Test() {/*общий код*/};
N>};
N>class Derived1 : public Base
N>{
N>public :
N> virtual Derived1* getSelf()
N> {return this;};
N> virtual void Test() {/*специфика для Derived1*/}
N>};
N>class Derived2 : public Base
N>{
N>public :
N> virtual Derived1* getSelf()
N> {return this;};
N> //а тут например, можно не писать свой Test(), будет использоваться Base::Test()
N>};
N>
N>Возможно в этом случае надобность в классе Tester отпадет.
Здравствуйте, Programador, Вы писали:
P>>Здравствуйте, dip_2000, P>Я так понял тестер желателен полиморфный, поскольку виртуал? Или это просто попытка решить какуюто другую задачу и он один
Здравствуйте, dip_2000, Вы писали:
_>Опять же попробую кодом проиллюстрировать: _>есть вот так ...
_> _>
ну если так не хочется Visitor применять, то я вот могу предложить только тот самый case() переделать, скажем вот так
class Base// сообщения
{
protected :
Base( int id ) : SomeTypeID( id ) {}
public:
int SomeTypeID;
virtual Base* getSelf()
{return this;}
virtual Base* querySelf( int type )
{ return NULL; }
};
template < class Owner >
struct QueryImpl : public Base
{
QueryImpl( int id ) : Base( id ) {}
virtual Base* querySelf( int type )
{ return (type == Owner::TYPE ? this->getSelf() : Base::querySelf(type) ); }
};
class Derived1 : public QueryImpl< Derived1 > //первое сообщение
{
typedef QueryImpl< Derived1 > base;
public :
static const int TYPE = 1;
Derived1() : base( TYPE ) {}
virtual Derived1* getSelf()
{return this;};
};
class Derived2 : public QueryImpl< Derived2 > //второе
{
typedef QueryImpl< Derived2 > base;
public :
static const int TYPE = 2;
Derived2() : base( TYPE ) {}
virtual Derived2* getSelf()
{return this;};
};
struct MessageProcessor
{
typedef boost::mpl::list< Derived1, Derived2 > messages_list;
typedef boost::mpl::end< messages_list >::type end_iterator;
typedef boost::mpl::begin< messages_list >::type begin_iterator;
void processDerived( Derived1 * ) {}
void processDerived( Derived2 * ) {}
template < class Iter >
bool process( Base * base )
{
typedef typename boost::mpl::deref<Iter>::type message_type;
Base * tmp = base->querySelf( message_type::TYPE );
if ( tmp != NULL )
{
this->processDerived( (message_type *)tmp );
// эквивалентно по идее такому
// this->processDerived( (message_type *)base );return true;
}
return process< typename boost::mpl::next< Iter >::type >( base );
}
template <>
bool process<end_iterator>( Base * base){ return false; }
void commonProcess( void * arg )
{
Base * base = (Base *)arg;
if ( this->process< boost::mpl::begin< messages_list >::type >( base ) )
{
// удалось обработать сообщение
}
else
{
// не получилось обработать
}
}
};
void test_messages()
{
MessageProcessor processor;
Derived1 der1;
Derived2 der2;
//Base base;
processor.commonProcess( static_cast< Base *>(&der1) );
processor.commonProcess( static_cast< Base *>(&der2) );
//processor.commonProcess( &base );
}
но сам принцип остается. ведь у Вас есть только базовый класс сообщения — Base, и что он такое — известно только в рантайме.
а вообще, можно же выделить интерфейс визитора, и именно о нем будут знать сообщения, а классу Tester только реализовать этот интерфейс.
class Derived1;
class Derived2;
struct IProcessor
{
virtual void process( Derived1 * ) = 0;
virtual void process( Derived2 * ) = 0;
};
class Base// сообщения
{
protected :
Base( int id ) : SomeTypeID( id ) {}
public:
int SomeTypeID;
virtual Base* getSelf()
{return this;}
virtual void startProcess( IProcessor * processor ) = 0;
};
class Derived1 : public QueryImpl< Derived1 > //первое сообщение
{
public :
static const int TYPE = 1;
Derived1() : Base( TYPE ) {}
virtual Derived1* getSelf()
{return this;};
virtual void startProcess( IProcessor * processor )
{
processor->process( this );
}
};
class Derived2 : public Base //второе
{
public :
static const int TYPE = 2;
Derived2() : Base( TYPE ) {}
virtual Derived2* getSelf()
{return this;};
virtual void startProcess( IProcessor * processor )
{
processor->process( this );
}
};