C++使用数组中的类执行函数

C++ execute function with class from array

本文关键字:执行 函数 数组 C++      更新时间:2023-10-16

我需要能够通过在数组中查找函数指针来调用作为类成员的函数。这个类将有做同样事情的子类,但如果它们不能为函数提供资金,则调用父类。为了简单起见,我删掉了大部分代码。剩余部分如下所示。

最终的测试是创建:

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

我想尽可能干净地构建它,这样鲍勃·马丁叔叔就会对我微笑。现在,我想他会狠狠地踢我一脚,所以如果能帮助重构它,让它变得更好,我将不胜感激。

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

我遇到了两个编译错误,但我一直无法解决。它们在代码中突出显示。

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

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

成为

enum {
CMD_EAT
, CMD_SLEEP
, CMD_MAKENOISE
, COMMAND_COUNT
};

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

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

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

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

command_info _commands[COMMAND_COUNT];

我们还可以从构造函数中删除actions参数。

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

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

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

virtual ~Animal() {}

我们已经接近尾声了——接下来是如何调用成员函数指针的问题。正确的方法(考虑到上述修改)是:

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

最后,您在程序结束时泄漏内存。

delete pAnimal;

然而,最好使用RAII进行资源管理。由于您使用的是AVR,并且没有可用的标准C++库,因此您可以定义一个简单的句柄类,类似于

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

完整的修订代码

注意:我注释掉了涉及Serial的行,这样我就可以在没有它的情况下编译了。

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上的样品

我重构了Animal类以去除大量噪声。还使用了C++11功能。如果你没有c++11,那么它可以很容易地被boost取代。我基本上已经改变了你注册的方式。此外,我删除了一些在当前环境下对我来说没有意义的东西,但可能对你很重要。

#include <iostream>
#include <map>
#include <memory>
#include <functional>
class Animal {
public:
enum Action {
CMD_EAT = 1,
CMD_SLEEP,
CMD_MAKENOISE
};
private:
const std::string _name;
std::map<Action, std::function<void(int)>> _actionsMap;
public:
Animal(const std::string& name) : _name(name) {
registerCommands();
}
//  Register all commands supported by this class
virtual void registerCommands() {
using namespace std::placeholders;
registerCommand(CMD_EAT,       std::bind(&Animal::eat, this, _1));
registerCommand(CMD_SLEEP,     std::bind(&Animal::sleep, this, _1));
registerCommand(CMD_MAKENOISE, std::bind(&Animal::makeNoise, this, _1));
}
void registerCommand(Action code, std::function<void(int)> cb) {
_actionsMap.emplace(code, cb);
}
void exec(Action action, int value) {
//Serial.print("Executing  ");
//Serial.print(code);
//TODO: Check if present in map
_actionsMap[action](value);
}
//  base class methods
virtual void eat(int times) { std::cout << "Eatn"; }
virtual void sleep(int times) { std::cout << "Sleepn"; }
void makeNoise(int times) {}
};
int main() {
//  Step 1: Create pointer to an instance of an animal object
std::unique_ptr<Animal> pAnimal(new Animal("Animal"));
pAnimal->exec(Animal::CMD_EAT, 1);
pAnimal->exec(Animal::CMD_SLEEP, 1);
return 0;
}