Проблема приведения к производному типу в C++

Я новичок в C++, но много лет работал с C#, однако здесь это мне не помогает! :)

Моя проблема: у меня есть класс Actor, из которого Ball и Peg вытекают из игры для iphone с объективом, над которой я работаю. Поскольку я проверяю конфликты, я хочу установить экземпляр Ball и Peg соответствующим образом в зависимости от фактического типа среды выполнения actorA или actorB. Мой код, который проверяет это следующим образом:

// Actors that collided
        Actor *actorA = (Actor*) bodyA->GetUserData();
        Actor *actorB = (Actor*) bodyB->GetUserData();

        Ball* ball;
        Peg* peg;
        if (static_cast<Ball*> (actorA)) { // true
            ball = static_cast<Ball*> (actorA);
        }
        else if (static_cast<Ball*> (actorB)) {
            ball = static_cast<Ball*> (actorB);
        }
        if (static_cast<Peg*> (actorA)) { // also true?!
            peg = static_cast<Peg*> (actorA);
        }
        else if (static_cast<Peg*> (actorB)) {
            peg = static_cast<Peg*> (actorB);
        }
        if (peg != NULL) {
            [peg hitByBall];
        }

Как только ball и peg установлены, я приступаю к запуску метода hitByBall (цель c).

Моя проблема действительно заключается в процедуре литья Ball отлично отбрасывает из actorA; первый оператор if (static_cast<>) вмешивается и устанавливает указатель ball соответствующим образом.

Второй шаг — присвоить peg соответствующий тип. Я знаю, что peg должен быть типом Peg, и я ранее знал, что это будет actorB, однако во время выполнения, определяя типы, я был удивлен, обнаружив, что на самом деле вмешался третий оператор if (static_cast<>) и установил это, это оператор if должен был проверить, был ли actorA Peg, а мы уже знаем, что actorA это Ball! Почему он встал здесь, а не в четвертом операторе if?

Единственное, что я могу предположить, это то, что кастинг работает иначе, чем в С#, и он обнаруживает, что actorA, который на самом деле имеет тип Ball, происходит от Actor, а затем, когда выполняется static_cast<Peg*> (actorA), он обнаружил, что Peg также происходит от Actor, так что это допустимо тестовое задание? Все могло сводиться к тому, что я неправильно понял использование static_cast. Как я могу достичь того, что мне нужно? :)

Меня действительно беспокоит то, что мне кажется длинной попыткой перебора здесь с кучей нелепых утверждений if. Я уверен, что есть более элегантный способ добиться простого приведения к Peg и приведения к Ball в зависимости от фактического типа, содержащегося в actorA и actorB.

Надеюсь, кто-то там может помочь! :) Большое спасибо.


person GONeale    schedule 22.05.2010    source источник
comment
Каков тип возврата GetUserData()?   -  person Marlon    schedule 22.05.2010
comment
Это будет Ball или Peg. Это может измениться.   -  person GONeale    schedule 22.05.2010
comment
static_cast так не работает. Вы ошибочно принимаете static_cast за dynamic_cast.   -  person sellibitze    schedule 22.05.2010


Ответы (3)


Поскольку это код Objective-C (а не C++, как следует из названия), почему бы просто не вызвать:

[actorA hitByBall];
[actorB hitByBall];

Обновлено: если объект, которому вы отправляете сообщение, nil, оно будет проигнорировано. Если объект, которому вы отправляете сообщение, не реализует hitByBall, вы получите исключение "селектор не распознан", если вы не поместили пустое определение в свой базовый класс (Actor).

Затем вы можете удалить свои объявления ball и peg и все static_cast (что было бы неправильно даже в C++).

person Johnsyweb    schedule 22.05.2010
comment
Спасибо, Джонси. После переосмысления моей реализации, как многие предлагали, я согласен с вами, после небольшого дополнительного исследования и осознания того, что все объективные методы c эффективно «виртуальны», тогда я просто не могу реализовать базовый метод и да, как вы говорите, просто реализуйте hitByBall для Peg и других классов будут игнорироваться. Спасибо. - person GONeale; 22.05.2010
comment
Это неверно в общем случае. Если объект не реализует метод, вы получите исключение, не распознаваемое селектором. Вероятно, вы оба говорите о переопределении реализации базового класса. - person JeremyP; 22.05.2010
comment
Спасибо @JeremyP: я соответственно уточнил свой ответ. - person Johnsyweb; 23.05.2010

Чтобы определить тип объекта во время выполнения, вы должны dynamic_cast, а не static_cast. Но вы действительно должны пересмотреть свой дизайн. Вероятно (я ничего не знаю о target-c), вам следует сделать hitByBall виртуальным методом в базовом классе и переопределить реализацию, если это необходимо, в производных классах. Затем вы можете вызвать метод без каких-либо приведений.

person Naveen    schedule 22.05.2010
comment
ошибка: невозможно выполнить dynamic_cast 'actorA' (типа 'struct Actor*') для типа 'struct Ball*' (исходный тип не является полиморфным) - person GONeale; 22.05.2010
comment
Вы можете опубликовать объявление структуры A и структуры Ball? - person Naveen; 22.05.2010
comment
Не используйте dynamic_cast, кроме как в тестовом коде. Если вам нужно узнать информацию о типах во время выполнения таким образом, помимо модульного теста, у вас, вероятно, есть недостаток дизайна. - Google, в поддержку вашего предложения пересмотреть дизайн - person Marlon; 22.05.2010
comment
Спасибо за ваши комментарии, ребята, я пересмотрел свой подход и выбрал полиморфный подход, как предложил Johnsyweb. Однако это по-прежнему вызывает у меня претензии к static_cast‹T›. Почему он возвращал true как для Ball, так и для Peg, когда это был тип Ball? - person GONeale; 22.05.2010
comment
static_cast не возвращал true, он возвращал указатель либо на actorA, либо на actorB. В отличие от dynamic_cast, при RTTI проверки во время выполнения не выполняются. - person Johnsyweb; 23.05.2010

Кажется, вы ищете -isKindOfClass. Если оба объекта соответствуют протоколу NSObject, вам нужно только проверить, какой из них относится к какому типу — фактическая передача сообщения не ограничивается статическим типом указателя:

if (   [actorA isKindOfClass:[Peg class ]] 
    && [actorB isKindOfClass:[Ball class]]) 
{
    [actorA hitByBall];
}
else if (   [actorB isKindOfClass:[Peg class ]]
         && [actorA isKindOfClass:[Ball class]]) 
{
    [actorB hitByBall];
}
person Georg Fritzsche    schedule 22.05.2010
comment
Ну, оба наследуют NSObject на своем корневом уровне, так что хороший момент. Я думал об этом, но я думаю, что после того, как я упорно работал с тестом типа С++, по какой-то причине я никогда не возвращался к нему. Спасибо, Георг. - person GONeale; 22.05.2010