Как можно реализовать автоматические вызовы вируальных функций сверху вниз по иерархии?
Например:
class A
{
public:
A();
virtual void MyFunction()
{
//...
};
virtual ~A();
};
class B : public A
{
B();
void MyFunction()
{
//...
A::MyFunction();
};
~B();
};
class C : public B
{
C();
void MyFunction()
{
//...
B::MyFunction();
};
~C();
};
A* _a=new C();
_a->MyFunction();
delete _a;
Так вот напрягает писать постоянно явные вызовы, есть ли более удачное решение этой проблемы в С++?
А>Так вот напрягает писать постоянно явные вызовы, есть ли более удачное решение этой проблемы в С++?
AFAIK, хорошего нет. Правда не понятны границы несчастья.
Насколько у тебя глубокая иерархия, сколько функций, сколько иерархий и т. д.
Например, если иерархия ОЧЕНЬ глубокая и разветвлённая, то можно извратиться, например, так:
template<typename D, typename B>
struct pad : B { virtual void MyFunction(); };
template<typename D, typename B>
void pad<D, B>::MyFunction()
{
B::MyFunctionImpl();
D::MyFunctionImpl();
}
class Base {
public:
virtual void MyFunction() { MyFunctionImpl(); }
void MyFunctionImpl() { ... }
};
class Dir1 : public pad<Dir1, Base> {
public:
void MyFunctionImpl() { ... }
};
class Dir2 : public pad<Dir2, Base> {
public:
void MyFunctionImpl() { ... }
};
class Dir3 : public pad<Dir3, Dir2> {
public:
void MyFunctionImpl() { ... }
};
ну и дальше можно сколь угодно продвинуто извращаться
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Аноним, Вы писали:
А>Хай!
А>Как можно реализовать автоматические вызовы вируальных функций сверху вниз по иерархии?
Вряд ли. Разве что можно посоветовать несколько автоматизировать указание базового класса:
А>Например:
А>
А>class A
А>{
А>public:
А>A();
А>virtual void MyFunction()
А>{
А> //...
А>};
А>virtual ~A();
А>};
А>class B : public A
А>{
А>typedef A BaseClass;
А>B();
А>void MyFunction()
А>{
А> //...
А> BaseClass::MyFunction();
А>};
А>~B();
А>};
А>class C : public B
А>{
А>typedef B BaseClass;
А>C();
А>void MyFunction()
А>{
А> //...
А> BaseClass::MyFunction();
А>};
А>~C();
А>};
А>A* _a=new C();
_a->>MyFunction();
А>delete _a;
А>
этот typedef писать в приватной секции, чтобы он был его не видели наследники.
А>Так вот напрягает писать постоянно явные вызовы, есть ли более удачное решение этой проблемы в С++?
А>Спасибо!
Здравствуйте, Roman Odaisky, Вы писали:
RO>Начать надо бы с того, что never derive from concrete classes и make non-leaf classes abstract (Meyers, Sutter). Иерархия здесь неправильная.
Возможно это иногда хороший совет, но вряд ли правило на многие случаи.
Покажите достаточно большую GUI библиотеку с ООП, следующую этому правилу.
Русский военный корабль идёт ко дну!
Re[2]: Virtual Deep Calls?
От:
Аноним
Дата:
03.07.08 10:04
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:
RO>Здравствуйте, Аноним, Вы писали: RO>Начать надо бы с того, что never derive from concrete classes и make non-leaf classes abstract (Meyers, Sutter). Иерархия здесь неправильная.
Здравствуйте, Alexander G, Вы писали:
RO>>Начать надо бы с того, что never derive from concrete classes и make non-leaf classes abstract (Meyers, Sutter). Иерархия здесь неправильная.
AG>Возможно это иногда хороший совет, но вряд ли правило на многие случаи. AG>Покажите достаточно большую GUI библиотеку с ООП, следующую этому правилу.
Кстати о гуи-библиотеках.
Есть две ситуации, когда нужно вызывать предка.
1) Когда наследник расширяет функциональность, так, что это расширение реализуется частично через функциональность предка.
Тут каждый случай уникален, и то — когда и с какими параметрами вызывать функцию предка — может здорово варьироваться. Так что автоматизация здесь будет злом.
2) Когда происходит обработка события (в ООП вызов метода — это синхронная посылка сообщения )
Функция, помимо собственно реакции, ещё и выступает диспетчером.
Тогда можно отделить код диспетчера от кода реакции, и придти к одному из сценариев:
— реакция базы, затем обязательно реакция наследника (так работают конструкторы и присваивания)
— реакция базы; затем, если реакция "нулевая", следует реакция наследника
— реакция наследника; затем, если она "нулевая", следует реакция базы
Так пишутся message maps. Естественно, что наборы параметров, специфичные для каждого из сообщений, для этого упаковываются в нечто единообразное — UINT/WPARAM/LPARAM, или в TEvent, и т.п.
Если наследование одиночное, то вызов базы всегда перед началом или всегда в конце — не составляет труда. Примеры — MFC и (если говорить не про С++) JavaScript в HTML.
Если наследование множественное, как например, в WTL — то в середину message map пихаются вызовы диспетчеров соответствующих баз и даже агрегатов.
Тут всё в руках программиста, но главное, что каркас диспетчера один, и именно в нём — а не в обработчиках конкретных сообщений — заложена политика цепочки вызовов.
Здравствуйте, Кодт, Вы писали:
К>Кстати о гуи-библиотеках.
...
Со всем согласен.
Но я о другом, а именно об ответе "Иерархия здесь неправильная."
"Правильная" иерархия для GUI библиотек, не содержащая наследования реализации будет очень неудобной.
Кстати в С++ приходится платить за возможность множественного наследования реализации:
A::MyFunction(); // в C++ вызываем нужного предка
inherited MyFunction(); // в Delphi вызываем единственного предка, не являющегося интерфейсом.inherited; // в Delphi вызываем унаследованный метод возвращающий void с тем же именем и параметрами.