C ++使用类从数组执行函数 [英] C++ execute function with class from array

查看:140
本文介绍了C ++使用类从数组执行函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要能够通过查找数组中的函数指针来调用作为类成员的函数。这个类将有一些子类做同样的事情,但如果他们不能资助函数,则调用父类。为了使事情简单,我已经剪掉了大部分的代码。剩下的内容如下所示。

I need to be able to call a function that is a member of a class by looking up the function pointer in an array. This class will have sub classes that do the same thing but call the parent class if they cannot fund the function. To make matters simple, I have cut out most of the code. What remains is shown below.

最终测试是创建:

1) Mammal : public Animal
1.1) Cat : public Mammal
1.2) Dog : public Mammal
2) Reptile : public Animal
2.1) Bird : public Reptile

我想尽可能干净地构建这个,所以鲍勃·马丁叔叔会对我微笑。现在,我想他只是给我一条裤子,所以任何帮助重构这使它更好,将深感赞赏。

I want to build this as cleanly as possible so Uncle Bob Martin would smile down on me. Right now, I think he would just give me a kick in the pants, so any help refactoring this to make it better would be deeply appreciated.

class Animal {
public:
#define CMD_EAT             1
#define CMD_SLEEP           2
#define CMD_MAKENOISE       3

private:
    const int _actions;
    const char* _name;

public:
    //  Define a pointer to a function within this class that takes
    //  an INT as its argument
    typedef void(Animal::*animalFunc)(int);

private:
    //  Define an array of pointers to action functions
    animalFunc _actionPointers[];  //<<< COMPILE ERROR: "incomplete type is not allowed"

    //  Define an array of action names
    char* _animalActions[];

public:
    Animal(int actions, char* name) : _actions(actions), _name(name) {
        _actionPointers[_actions] = NULL;
        _animalActions[_actions] = NULL;
        registerCommands();
    }

    //  Define an array of pointers to action functions
    //animalFunc animalCommands[MAX_ANIMAL_CMD];

    //  Register all commands supported by this class
    virtual void registerCommands() {
        registerCommand(CMD_EAT, "EAT", &Animal::eat);
        registerCommand(CMD_SLEEP, "SLEEP", &Animal::sleep);
        registerCommand(CMD_MAKENOISE, "MAKE NOISE", &Animal::makeNoise);
    }

    void registerCommand(int code, char* action, void (Animal::*animalFunc)(int)) {
        _animalActions[code - 1] = action;
        _actionPointers[code - 1] = animalFunc;
    }

    void exec(int code, int value) {
        Serial.print("Executing  ");
        Serial.print(code);
        *(this->_actionPointers[code])(value);  //<<< THIS DOESN'T COMPILE
    }

    const char* getName() {
        return _name;
    }

    //  base class methods
    virtual void eat(int times) {}

    virtual void sleep(int times) {}

    void makeNoise(int times) {}
};

void main() {
    //  Step 1: Create pointer to an instance of an animal object
    Animal *pAnimal = new Animal(3, "ANIMAL");
    pAnimal->exec(CMD_EAT, 1);
    pAnimal->exec(CMD_SLEEP, 1);
}



我收到两个编译错误, 。它们在代码中突出显示。

I am getting two compile errors that I haven't been able to get around. They are highlighted in the code.

推荐答案

可以做的第一件事是替换 #define 和一个枚举,并添加一个总命令计数。

First thing that can be done is to replace #defines with an enum, and add a total command count.

#define CMD_EAT             1
#define CMD_SLEEP           2
#define CMD_MAKENOISE       3

变为

enum {
     CMD_EAT
     , CMD_SLEEP
     , CMD_MAKENOISE
     , COMMAND_COUNT
};

接下来,我们应该确保代码是const正确的。因为我们使用字符串常量,所有的字符串变量和函数参数应该是 char const * 而不是 char *

Next, we should make sure the code is const-correct. Since we're using string constants, all the string variables and function arguments should be char const* rather than char*.

之后,我们可以在一个结构中组合函数指针和名称,因为它们属于一起。注意,由于我们有一个成员函数指针的typedef,我们可以使用。

After this, we could combine the function pointer and the name in one structure, since they belong together. Notice that since we have a typedef for the member function pointer, we can use that.

struct command_info
{
    animalFunc handler;
    char const* name;
};

由于我们现在知道编译时的命令数,而且我们有上面的结构,有一个固定大小的数组:

Since we now know the number of commands at compile time, and we have the above structure, we can have a single fixed-size array:

command_info _commands[COMMAND_COUNT];

我们也可以删除 actions 构造函数。

We can also drop the actions parameter from the constructor.

由于我们有一个固定大小的数组,所以在访问数组之前验证索引很重要:

Since we have a fixed size array, it's important to validate the index before accessing the array:

if (code < COMMAND_COUNT) { //...

接下来,你有虚方法,所以你的类应该有一个虚拟析构函数:

Next, you have virtual methods, so your class should also have a virtual destructor:

virtual ~Animal() {}

我们即将结束 - 接下来有一个问题, =https://isocpp.org/wiki/faq/pointers-to-members#dotstar-vs-arrowstar =nofollow>调用成员函数指针。正确的方式(上面提到的修改)是:

We're getting close to the end -- next there's a problem with how you invoke the member function pointer. The correct way (with the above mentioned modifications in mind) is:

(this->*_commands[code].handler)(value);

最后,你的程序结束时会泄漏内存。

Finally, you leak memory at the end of your program.

delete pAnimal;

但是,最好使用RAII进行资源管理。因为你是AVR,没有标准C ++库可用,你可以定义一个简单的句柄类,

However, it would be better to use RAII for resource management. Since you're on AVR and don't have the Standard C++ Library available, you could just define a simple handle class, something along the lines of

struct animal_ptr {
    animal_ptr(Animal* a) : ptr(a) {}
    ~animal_ptr() { delete a; }
    Animal* ptr;
}






h2>

注意:我注释掉了 Serial 的行,这样我就可以不用它编译。<​​/ p>


Complete revised code

NB: I commented out the lines involving Serial, so that I can compile without it.

class Animal 
{
public:
    enum {
        CMD_EAT
        , CMD_SLEEP
        , CMD_MAKENOISE
        , COMMAND_COUNT
    };

    //  Define a pointer to a function within this class that takes
    //  an INT as its argument
    typedef void(Animal::*animalFunc)(int);
    struct command_info
    {
        animalFunc handler;
        char const* name;
    };

public:
    Animal(char const* name)
        : _name(name)
    {
        registerCommands();
    }

    //  Register all commands supported by this class
    virtual void registerCommands() {
        registerCommand(CMD_EAT, "EAT", &Animal::eat);
        registerCommand(CMD_SLEEP, "SLEEP", &Animal::sleep);
        registerCommand(CMD_MAKENOISE, "MAKE NOISE", &Animal::makeNoise);
    }

    void registerCommand(int code, char const* action, animalFunc fn) {
        if (code < COMMAND_COUNT) {
            _commands[code].name = action;
            _commands[code].handler = fn;
        }
    }

    void exec(int code, int value) {
        if (code < COMMAND_COUNT) {
            //Serial.print("Executing  ");
            //Serial.print(code);
            (this->*_commands[code].handler)(value);
        }
    }

    char const* getName() {
        return _name;
    }

    //  base class methods
    virtual void eat(int times) {}

    virtual void sleep(int times) {}

    void makeNoise(int times) {}


private:
    char const* _name;

    //  Define an array of pointers to action functions
    command_info _commands[COMMAND_COUNT];
};

int main() {
    //  Step 1: Create pointer to an instance of an animal object
    Animal *pAnimal = new Animal("ANIMAL");
    pAnimal->exec(Animal::CMD_EAT, 1);
    pAnimal->exec(Animal::CMD_SLEEP, 1);
    delete pAnimal;
}

Coliru上的示例

这篇关于C ++使用类从数组执行函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆