2012-02-08 2 views
3

Я делаю небольшую игру на C++, и я обнаруживаю указатели на функции класса. У меня нет никакой идеи, чтобы они работали правильно, но вот моя попытка.C++ Member Function Pointer

// A struct where the function pointer will be stored for the call 
// By the way, is there a way to do the same thing with classes ? 
// or are structs still fine in C++ ? (Feels like using char instead of string) 
typedef struct  s_dEntitySpawn 
{ 
    std::string  name; 
    void   (dEntity::*ptr)(); 
} t_dEntitySpawn; 

// Filling the struct, if the entity's classname is "actor_basicnpc", 
// then I would like to do a call like ent->spawnBasicNPC 
t_dEntitySpawn  dEntitySpawns[] = { 
    { "actor_basicnpc", &dEntity::spawnBasicNPC }, 
    { 0, 0 } 
}; 

// This is where each entity is analyzed 
// and where I call the function pointer 
void   dEntitiesManager::spawnEntities() 
{ 
    dEntity  *ent; 
    t_dEntitySpawn *spawn; 

    [...] 

     // It makes an error here, feels very weird for me 
     if (!spawn->name.compare(ent->getClassname())) 
     ent->*spawn.ptr(); 

    [...] 
} 

Не могли бы вы дать мне хороший совет о правильном способе их реализации?

С уважением.

+1

Используйте вместо этого интерфейсы. т. е. создать базовый класс с виртуальным методом, а затем создать различные классы с соответствующей реализацией этого метода. Делает код более удобным для чтения и поведения, как вам нужно. –

+0

Точный дубликат [Как вызвать указатель на функцию-член, когда это элемент данных класса?] (Http: // stackoverflow.com/questions/6316751/how-to-invoke-pointer-to-member-function-when-its-a-class-data-member) – iammilind

+1

Я настоятельно рекомендую вам использовать хороший стиль именования, чтобы другой мог легко понять вы код –

ответ

5

Я думаю, что линия, которую Вы ищете,

(ent->*(spawn->ptr))(); 

Давайте проанализируем это. Во-первых, мы должны добраться до фактической функции члена указателя, который

spawn->ptr 

Так, здесь, spawn является указателем, и мы должны использовать -> выбрать ptr поле.

После того, как мы есть, что мы должны использовать оператор указателя на член отбора сказать ent, чтобы выбрать соответствующую функцию члена:

ent->*(spawn->ptr) 

Наконец, чтобы вызвать функцию, мы должны сказать C++ для вызова этой функции-члена. Из-за проблем оператора очередностью в C++, вы должны сначала скобки все выражение, которое вычисляет функции члена, поэтому мы имеем

(ent->*(spawn->ptr))(); 

Для чего это стоит, это один из самых странных строк кода C++, что я Некоторое время я видел. :-)

На совершенно несвязанной ноте, потому что вы используете C++, я бы избегал использовать typedef struct. Просто скажите

struct t_dEntitySpawn { 
    std::string name; 
    void (dEntity::*ptr)(); 
}; 

Надеюсь, что это поможет!

+0

+1 правильный ответ. Однако этот вопрос является дубликатом моего предыдущего вопроса. – iammilind

+0

Спасибо большое! Теперь это более понятно, я действительно понимаю, у меня хороший день! –

3

Правильный способ программирования в этом случае - прекратить программирование, подобное C на C++, и начать использовать функции C++, такие как виртуальные функции. :-P

Я говорю «программирование как C», потому что то, что вы делаете, похоже на то, как программисты C реализуют polymorphism на языке C. Там нет необходимости делать это на C++, потому что C++ поставляется со встроенной поддержкой полиморфизма, который был разработан, чтобы помочь вам решить вашу ситуацию. Виртуальные функции - это то, как C++ реализует полиморфизм.

Не говоря уже о том, что в этом случае полиморфизм с помощью указателей функций может быть намного быстрее, чем у вас сейчас, поскольку для виртуальных функций C++ не требуется сопоставление строк.

Имеются варианты использования указателей функций-членов. Эта ситуация не одна из них. Тем более, что он скрывает намерение вашего кода. (Помните, что код для людей читается!)

class EntitySpawn 
{ 
public: 
    void spawn_entity() 
    { 
     spawn(); 
    } 

private: 
    // std::string name; // Not necessary for polymorphism to work 
    virtual void spawn() = 0; 
}; 

class ActorBasicNPC : public EntitySpawn 
{ 
private: 
    virtual void spawn() { /* implementation specific to ActorBasicNPC */ } 
}; 

void test_spawn(EntitySpawn& es) 
{ 
    // This will call the correct implementation of spawn(), 
    // even though we only got a reference to an EntitySpawn. 
    es.spawn_entity(); 
} 

int main() 
{ 
    ActorBasicNPC npc; 
    // Calls ActorBasicNPC::spawn() even though only a 
    // reference to EntitySpawn was passed. 
    test_spawn(npc); 
}; 
+0

+1 для обеспечения правильного пути для этого. – templatetypedef

+0

Большое спасибо, я ясно вижу, что это лучший способ организовать это, я полностью забыл о виртуальных играх, и я чувствую, что он лучше подходит для языка C++. –

+0

+1 справа. Мы в значительной степени следуем заводской схеме. У вас должен быть экземпляр на заводе (т. Е. Любой экземпляр, который реализует EntitySpawn) до того, как он сможет использовать фабрику. Нет никакого вреда в управлении списком заводов, чтобы создать список сущностей. –