Нет, не получается
Попробовал через указатели на функции все это реализовать, но функции обрабатывающие разные действия имеют разные параметры (чтобы отслеживать окончание действия). Привести их все к одному виду func(void) не получается
Здравствуйте, Sni4ok, Вы писали:
S>Здравствуйте, Korchy, Вы писали:
K>>Или можно по другому как-то?
S>используйте статические интегральные константы
S>
S>class1 {
S>public:
S>static const int stay = 0;
S>static const int run = 1;
S>};
S>class2:public class1 {
S>public:
S>static const int jump = 2;
S>};
S>
ужас! это каждй кто наследует от таких классов должен будет просмотреть всю иерархию, дабы найти какеи константы есть уже?
но в енумами проблема таже самая будет
тут беда не в енумах а в архитектуре проекта
Здравствуйте, Korchy, Вы писали:
K>Нет, не получается K>Попробовал через указатели на функции все это реализовать, но функции обрабатывающие разные действия имеют разные параметры (чтобы отслеживать окончание действия). Привести их все к одному виду func(void) не получается
ну в контейнере должные лежать nullary functors (ну это допустим легко устроить используя рантаймный полиморфизм)
фича в том что то что там буит лежать должно уже иметь в себе связанные параметры, т.е. на момент push_back все параметры уже известны и сохранены вв наследниках полиморфной базы до момента вызова... засада в том что нужно иметь несколько специализаций (под разное число параметров) некой функции, которая буит возвращать nullary functor (наследник нашей базы) (c уже связанными параметрами). в принципе такое бустерным препроцессором делается на раз два ну или руками копипакостничается еси у тя 1-2-3 параметра в твои методы -- собсна это как устроен boost::bind внутри (ну +\- так) -- твоя задача переизобреати валасипед (не боясь при этом квадраатных колес которые неизбежно у тебя получатся)... ну или ждать С++0х
Да, получается что-то монструозное. Буст мне не хотелось бы использовать т.к. программа ограничена по скорости выполнения, а буст славится своей медленностью. В итоге, лучше обойдусь простыми дефайнами.
Как бы вы (хотя бы в общих чертах) порекомендовали ее реализовывать?
вариантов много, то точно не стал бы делать человека и танк производными от одного класса... движение человека и движение танка это не одно и то же
в принципе действия должны описываться методами, а не одним методом и параметром что это за действие..
а дальше не понятно зачем в цикле что то проверяется, если узнать что объект свободен или что то делает, то и нужен именно такой метод в базовом классе, если что б выполнялось само действие, то нуден метод Do() в базовом классе, а в объектах хранить что конкретно они должны делать, то биш вызвывая метод eat() у тигра, он в свой стек команд помещает что должен кушать а в методе do() он из стека знает что ему делать...
но это все так, задача то не ясна
Вообще перечисление — не класс и его "расширять" нельзя.
B C++ enum интерпретируется просто как константы определенные в данной области видимости.
таким образом элементы вашего енума — константы внутри данного класса.
естественно, что в другом классе нельзя написать нечто, что определит новые константы внутри старого класса.
строго говоря, чтобы такое реализовать, пришлось бы устраивать динамические проверки типа енума, чтобы внутри кода работающего с актуальным — расширенным енумомм, можно было использовать дополнительные значения, а вне его — нельзя. но енум — скалярный тип, и у него нет способа определить актуальный тип. тип всегда строго тот, как его поисали при декларации.
для того чтобы расширять типы, нужно делать навороты в виде классов.
если говорить более теоретически, при расширении перечисления например так
формальный синтаксис
enum A {One, Two};
enum B {enum A, Three}; //то есть построили тип как совокупность из старого енума и нового значения
обьект типа B несовместим с обектом типа A.
то есть присваивание обьекту типа А значения B некорректно. Ибо обьект типа А в таком случае может иметь значение Three, которое для него никак не определено. проверка корректности значения типа A будет показывать нарушение опрделения данного типа — что у него могут быть только значения One или Two.
пишите просто константы, а тип сделайте как какой-нить int.
либо в базовом классе пишите все возвожные варианты енума сразу.
Здравствуйте, Korchy, Вы писали:
K>В этом случае class2::Action не будет иметь значений stay и run, а только jump=2 и sit=3. Мне же нужно чтобы в class2::Action были все четыре значения.
Используйте везде int в качестве принимающего, тогда такой проблемы не будет.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
K>когда я наследую от него другой класс, объект которого должен выполнять еще одно действие (допустим, прыгать), как мне добавить его в список enum?
Лучше всего никак. Попытка делать подобное возникает из-за логической ошибки. Это как бы "вывернутый полиморфизм". На самом деле, наследование енума и смысла-то не имеет: базовый класс может обрабатывать только свои константы, а в производном имеет смысл обрабатывать только новые константы, таким образом наследование просто бессмысленно.
Тебе необходимо выделить общие свойства всех объектов, обозначаемых перечислением.
Разве здесь уместен статический полиморфизм?
имхо классический шаблон стратегия
class Action
{}
class ActionRun: public Action
{}
class Actor
{
virtual void Start(Action& a)=0;
virtual void Stop(Action& a)=0;
}
class Creature: public Actor
{
virtual void Run(ActionRun& a);
virtual void Start(Action& a);
virtual void Stop(Action& a);
}
примерно так — т.е. вместо enum виртуальный тип с произвольными параметрами, с некоторым интерфейсом.
Просто enum соответсвует тип с единственным int параметром. Что не мешает при необходимости его расширить.
Здравствуйте, Korchy, Вы писали:
K>моя задача состоит в том, чтобы реализовать удобное управление объектами. Каждый объект описывается классом. Например класс автомобиля Car, человека Human. Каждый из них может выполнять разные действия. Машина — двигаться вперед и назад, человек тоже двигаться и прыгать. Действия могут комбинироваться (прыжок + движение вперед). Чтобы это реализовать мне пришло в голову только сделать массив действий vector в который я заношу что список действий. В программе я каждую секунду пробегаю этот список и проверяю, какие действия там есть и еще не завершились и через case их обрабатываю. K>for(std:vector<Action*>:iterator i;begin;end) K>switch case *i->id = run...
K>действия я поименовал через enum просто для наглядности. Мне хотелось сделать некий перечень действий, в который я в каждом классе мог бы добавлять новые действия (как прыжки для человека) и переопределив функцию-обработчик добавить в нее новый case для обработки нового действия. enum же я попытался использовать чтобы хотя бы внутри класса не думать о том, что 1 — это прыжок, а 455 — "тройной тулуп" и т.д.
Ты не правильно решаешь проблему. На мой взгляд тут будет уместен паттёрн Посетитель (хотя, если иерархия объектов, над которыми выполняются действия не стабилен и очень часто меняется, то надо думать)
Как минимум, можно однозначно выделить 2 иерархии классов: action и item оба они абстракнтые, от них наследовать классы действий и классы объектов действий.
Здравствуйте, Head Ache, Вы писали:
HA>Разве здесь уместен статический полиморфизм?
мы (ну те из нас кто не телепат) не знаем что здесь уместнее ... аффтар не ракрывает деталей использования
я просто предложил вариант со своими плюсами и минусами... аффтар выберет то что по его мнению ему больше подходит
Вопрос в продолжение использования такого подхода:
у меня есть список действий std::vector<Action*> Actions
в нем хранятся действия, которые выполняются на данный момент
Каким образом мне найти нужное действие в списке, чтобы, допустим, его удалить (прекратить выполнять)? Через dynamic_cast проверять на соответсвие нужному типу (Action или ActionRun)? dynamic_cast достаточно долгий при длинных ветвях наследований, поэтому не хотелось бы его использовать. Или можно как-то еще?
Здравствуйте, Korchy, Вы писали:
K>Вопрос в продолжение использования такого подхода: K>у меня есть список действий std::vector<Action*> Actions
1. Использовать vector< shared_ptr<Action> >. Почему так — читать "effective stl".
K>в нем хранятся действия, которые выполняются на данный момент K>Каким образом мне найти нужное действие в списке, чтобы, допустим, его удалить (прекратить выполнять)? Через dynamic_cast проверять на соответсвие нужному типу (Action или ActionRun)? dynamic_cast достаточно долгий при длинных ветвях наследований, поэтому не хотелось бы его использовать. Или можно как-то еще?
2. Это опять частный случай: действительно ли тип элемента — единственный критерий и всегда им будет?
Изучить ситуацию при которой необходимо удаление. Добавить в базовый класс виртуальную функцию вроде bool CheckDelete() или OnActivate() и т.п. Обработать в производных классах.
А может, лучше это будет невиртуальная функция, проверяющая переменную в базовом классе. Которая в производных классах когда-то меняется.
Для удаления сделать предикат, проверяющий эту функцию.
Здравствуйте, Korchy, Вы писали:
K>Вопрос в продолжение использования такого подхода: K>у меня есть список действий std::vector<Action*> Actions K>в нем хранятся действия, которые выполняются на данный момент K>Каким образом мне найти нужное действие в списке,..
Зависит от того как именно и что именно ты делаешь.
Если, например, члены той иерархии, что у тебя была изначально, растут из IItem, а IAction выглядит как-то так:
то действия сами являются ID себя. Нопример можно использовать IAction* в качестве ID...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском