C++ 使用数组中的 class 执行函数
C++ execute function with class from array
我需要能够通过在数组中查找函数指针来调用作为 class 成员的函数。这个 class 将有子 classes 做同样的事情,但如果他们不能为该功能提供资金,则调用父 class。为了简单起见,我删掉了大部分代码。剩下的如下图。
终极考验是创造:
1) Mammal : public Animal
1.1) Cat : public Mammal
1.2) Dog : public Mammal
2) Reptile : public Animal
2.1) Bird : public Reptile
我想尽可能干净地构建它,这样 Bob Martin 叔叔就会对我微笑。现在,我想他只会刺激我一下,所以如果能帮助我重构它以使其变得更好,我们将不胜感激。
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);
}
我遇到了两个无法解决的编译错误。它们在代码中突出显示。
我重构了 Animal class 以移除大量噪音。还使用了 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 << "Eat\n"; }
virtual void sleep(int times) { std::cout << "Sleep\n"; }
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;
}
可以做的第一件事是用枚举替换 #define
s,并添加总命令数。
#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) { //...
接下来,你有虚方法,所以你的class也应该有一个虚析构函数:
virtual ~Animal() {}
我们接近尾声 -- 接下来是您 invoke the member function pointer 的问题。正确的方法(考虑到上述修改)是:
(this->*_commands[code].handler)(value);
最后,您在程序结束时泄漏了内存。
delete pAnimal;
不过,资源管理还是用RAII比较好。由于您使用的是 AVR 并且没有可用的标准 C++ 库,因此您可以定义一个简单的句柄 class,类似于
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;
}
我需要能够通过在数组中查找函数指针来调用作为 class 成员的函数。这个 class 将有子 classes 做同样的事情,但如果他们不能为该功能提供资金,则调用父 class。为了简单起见,我删掉了大部分代码。剩下的如下图。
终极考验是创造:
1) Mammal : public Animal
1.1) Cat : public Mammal
1.2) Dog : public Mammal
2) Reptile : public Animal
2.1) Bird : public Reptile
我想尽可能干净地构建它,这样 Bob Martin 叔叔就会对我微笑。现在,我想他只会刺激我一下,所以如果能帮助我重构它以使其变得更好,我们将不胜感激。
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);
}
我遇到了两个无法解决的编译错误。它们在代码中突出显示。
我重构了 Animal class 以移除大量噪音。还使用了 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 << "Eat\n"; }
virtual void sleep(int times) { std::cout << "Sleep\n"; }
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;
}
可以做的第一件事是用枚举替换 #define
s,并添加总命令数。
#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) { //...
接下来,你有虚方法,所以你的class也应该有一个虚析构函数:
virtual ~Animal() {}
我们接近尾声 -- 接下来是您 invoke the member function pointer 的问题。正确的方法(考虑到上述修改)是:
(this->*_commands[code].handler)(value);
最后,您在程序结束时泄漏了内存。
delete pAnimal;
不过,资源管理还是用RAII比较好。由于您使用的是 AVR 并且没有可用的标准 C++ 库,因此您可以定义一个简单的句柄 class,类似于
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;
}