C++ Member Function Pointers

2019-05-14 14:10发布

问题:

I'm doing a little game in C++ and I'm discovering the class members function pointers. I don't have any idea to make them work in the right way, but here is my attempt.

// 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();

    [...]
}

Could you please give me a good advice about the right way to implement them ?

Best regards.

回答1:

I think that the line you're looking for is

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

Let's dissect this. First, we need to get to the actual member function pointer, which is

spawn->ptr

Since, here, spawn is a pointer, and we have to use -> to select the ptr field.

Once we have that, we need to use the pointer-to-member-selection operator to tell ent to select the appropriate member function:

ent->*(spawn->ptr)

Finally, to call the function, we need to tell C++ to invoke this member function. Due to operator precedence issues in C++, you first have to parenthesize the entire expression that evaluates to the member function, so we have

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

For what it's worth, this is one of the weirdest lines of C++ code that I've seen in a while. :-)

On a totally unrelated note, because you're using C++, I would avoid using typedef struct. Just say

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

Hope this helps!



回答2:

The right way to program in this case is to stop programming like C in C++ and start using C++ features like virtual functions. :-P

I say "programming like C" because what you're doing resembles how C programmers implement polymorphism in the C language. There's no need to do that in C++ because C++ comes with built-in support for polymorphism, which was designed to help solve your situation. Virtual functions are how C++ implements polymorphism.

Not to mention that in this case, polymorphism via function pointers can be much faster than what you have now since string comparison is not required for C++ virtual functions to work.

There are use cases for member function pointers. This situation is not one of them. Especially since it obscures the intent of your code. (Remember, code is there for humans to read!)

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);
};