Подскажите, пожалуйста, есть класс, объект которого может выполнять 2 действия (стоять и бежать), названия которых я указываю в перечислении
class1 {
public:
enum Action {stay,run};
}
когда я наследую от него другой класс, объект которого должен выполнять еще одно действие (допустим, прыгать), как мне добавить его в список enum? Только переопределяя перечисление заново?
Или можно по другому как-то? А если классов-наследников много, stay и run должны копироваться в каждый? Или здесь вообще enum не к месту и нужно использовать что-то другое?
K>Или можно по другому как-то? А если классов-наследников много, stay и run должны копироваться в каждый? Или здесь вообще enum не к месту и нужно использовать что-то другое?
по стандарту, размер энума (сколько бит на него выделить) определяется при определении. То есть компилятор имеет право выделить, скажем, 8 бит, если исходно все элементы в них укладываются. Соответственно в дальнейшем это ограничение не преодолеть.
я не оч понимаю зачем этим объектам enum... чтоли он буит передваться как параметр в какуюнить виртуальную функцию?? типа do_action(run) или там do_action(jump)??
т.к. описания задачи нет то в голову приходит оч много вариантов рассказывать которые бессмысленно не зная зачем все это надо...
ну а по теме вопроса... ну да: в enum нельзя добавлять значения в наследниках... по сути ты определяешь новый enum c таким же именем что в последующем заставить тебя пользоваться full qualified именами для доступа к ним, что в конечно итоге приведет к аццкой путанице ибо run буит у базового класса и другие runы будут у наследников... кроме того это потребует усилий по синхронизации значений этих runов ибо ничто не помешает тебе придать им (случайно в том числе) разные значения... -- вопщем попахивает проблемами %)
доопределять можно например mplные сиквенсы -- ну т.е. в базовом классе заводим
struct base
{
typedef boost::mpl::vector<run, stay> actions;
};
и в наследниках делаем
struct child : public base
{
typedef boost::mpl::push_back<base::actions, jump> actions;
};
где действия run, stay, jump могут быть как просто пустыми структурами (ну типо тэги) так и например typedefами на boost::mpl::int_<> если хочется какогото числового значения...
ну а в целом чтобы полноценно рассказать о других способах надо знать чего ты хочешььь сделать...
Здравствуйте, Korchy, Вы писали:
K>Подскажите, пожалуйста, есть класс, объект которого может выполнять 2 действия (стоять и бежать), названия которых я указываю в перечислении K>... K>Или можно по другому как-то? А если классов-наследников много, stay и run должны копироваться в каждый? Или здесь вообще enum не к месту и нужно использовать что-то другое?
Ну можно так:
class class1 {
public:
enum Action {stay,run};
static const int LastAction = run;
};
class class2:public class1 {
public:
enum Action {jump=LastAction+1,sit};
};
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
моя задача состоит в том, чтобы реализовать удобное управление объектами. Каждый объект описывается классом. Например класс автомобиля Car, человека Human. Каждый из них может выполнять разные действия. Машина — двигаться вперед и назад, человек тоже двигаться и прыгать. Действия могут комбинироваться (прыжок + движение вперед). Чтобы это реализовать мне пришло в голову только сделать массив действий vector в который я заношу что список действий. В программе я каждую секунду пробегаю этот список и проверяю, какие действия там есть и еще не завершились и через case их обрабатываю.
for(std:vector<Action*>:iterator i;begin;end)
switch case *i->id = run...
действия я поименовал через enum просто для наглядности. Мне хотелось сделать некий перечень действий, в который я в каждом классе мог бы добавлять новые действия (как прыжки для человека) и переопределив функцию-обработчик добавить в нее новый case для обработки нового действия. enum же я попытался использовать чтобы хотя бы внутри класса не думать о том, что 1 — это прыжок, а 455 — "тройной тулуп" и т.д.
В этом случае class2::Action не будет иметь значений stay и run, а только jump=2 и sit=3. Мне же нужно чтобы в class2::Action были все четыре значения.
В классе-наследнике, если не ошибаюсь, снова будет выполнена операция определения размера и будет выделено столько памяти, сколько нужно. Проблема в том, что для этого enum нужно копировать целиком из класса-предка и добавлять значения в него. Получается жуткое дублирование кода и путаница, как следствие. Мне хотелось этого избежать.
Здравствуйте, Korchy, Вы писали:
K>Подскажите, пожалуйста, есть класс, объект которого может выполнять 2 действия (стоять и бежать), названия которых я указываю в перечислении K>
K>когда я наследую от него другой класс, объект которого должен выполнять еще одно действие (допустим, прыгать), как мне добавить его в список enum? Только переопределяя перечисление заново? K>
K>Или можно по другому как-то? А если классов-наследников много, stay и run должны копироваться в каждый? Или здесь вообще enum не к месту и нужно использовать что-то другое?
Есть на codeproject тема SmartEnum, но мне кажется не слишком удачной.
А вообще традиционно используют константы, или как уже сказанно инициализируют перечесляемый тип начиная со значения конечного элемента "наследуемого".
class base {
enum EAction { super = 0 };
static const stay = super+1;
// etc...virtual some_method(EAction act) =0;
};
Здравствуйте, 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 — "тройной тулуп" и т.д.
ну вот что мне видится: ты имеешь набор разрозненных объектов объединять которые в единую иерархию нет никогого смысла ибо и свойства и действия у них оч разные. одно из решений на вскидку:
просто заводим контейнер с действиями которые дребуется выполнять и bindим в него объекты и методы. если приспичит можно писать более сложные функторы кторые будут действовать над несколькими объектами одновременно или выполнять несколько действий над одним (ну типа бежать и прыгать) -- как захочется в общем...
таким образом никакие енумы нафиг не нужны и соответсна не придется их синхронизировать а также не нада буит постоянно модифицировать switch втыкая в него реакцию на новые действия и вообще от самих объектов ничего не требуется (ну разве что наличие экземпляра)
Здравствуйте, 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 — "тройной тулуп" и т.д.
Про define и другие варианты тут уже рассказали.
Еще вариант. Держать все возможные движения в одном enum. Каждый объект обрабатывает только те события, которые он умеет обрабатывать.
Здравствуйте, zaufi, Вы писали:
Z>ну вот что мне видится: ты имеешь набор разрозненных объектов объединять которые в единую иерархию нет никогого смысла ибо и свойства и действия у них оч разные.
Я бы не был таким категоричным. Потом например появится легковая машина, грузовая и танк, которые будут уметь обрабатывать одинаковые события типа вперед, назад, стой, и поворот на столько то градусов.
Здравствуйте, Turyst, Вы писали:
T>Здравствуйте, 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 — "тройной тулуп" и т.д.
T>Про define и другие варианты тут уже рассказали. T>Еще вариант. Держать все возможные движения в одном enum. Каждый объект обрабатывает только те события, которые он умеет обрабатывать.
Тоесть не делать enum членом класса.
Здравствуйте, Turyst, Вы писали:
T>Здравствуйте, zaufi, Вы писали:
Z>>ну вот что мне видится: ты имеешь набор разрозненных объектов объединять которые в единую иерархию нет никогого смысла ибо и свойства и действия у них оч разные.
T>Я бы не был таким категоричным. Потом например появится легковая машина, грузовая и танк, которые будут уметь обрабатывать одинаковые события типа вперед, назад, стой, и поворот на столько то градусов.
не будем забегать вперед
из того что сказал топикстартер этого вовсе не следует. а даже если и появится то грузовики с танками и машинами врядли окажутся в одной иерархии с человеком (и возможно животными, растениями, планетами или там субатомными частицами)
D>по стандарту, размер энума (сколько бит на него выделить) определяется при определении. То есть компилятор имеет право выделить, скажем, 8 бит, если исходно все элементы в них укладываются. Соответственно в дальнейшем это ограничение не преодолеть.
Нет, не получается
Попробовал через указатели на функции все это реализовать, но функции обрабатывающие разные действия имеют разные параметры (чтобы отслеживать окончание действия). Привести их все к одному виду 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...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском