Assume class Animal which does Eat, Sleep and Make Noise.
Assume class Mammal : public Animal which also does MakesBaby
Assume that Mammal also does Eat, Sleep and Make Noise which it inherits from Animal.
此代码扩展自this question中提供的代码,Dan Masek昨天回答了该代码。
此代码的问题是:
我希望这段代码尽可能优秀,但我被困住了。
我认为两个类之间的相似性围绕着定义函数的typedef,而函数是指向类中成员的指针。我试着模仿这个,但遇到了很多编译错误,从而表明我完全无法理解如何解决这个问题。因此这篇文章。
对于那些看过我上一篇文章的人,我添加了一个#ifdef来禁用打印(这是Arduino代码,所以STD不可用),我尽可能地添加了断言。我还添加了哺乳动物课程。
// Set to false to suppress Serial.print output
#define TRACING true
class Animal
{
public:
enum {
CMD_EAT = 1
, 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); // U THING THIS IS THE PROBLEM - CAN IT BE TEMPLATED? IF SO, I COULD NOT FIGURE OUT THE SYNTAX TO MAKE IT WORK
struct commandInfo
{
unsigned int code;
animalFunc handler;
char const* name;
};
Animal(char const* name) : _name(name) {
registerCommands();
}
// Register all commands supported by this class
virtual void registerCommands() {
#if TRACING
Serial.print(F("Registering "));
Serial.print(getName());
Serial.println(F(" Actions:"));
#endif
unsigned int index = 0;
registerCommand(index++, CMD_EAT, "EAT", &Animal::eat);
registerCommand(index++, CMD_SLEEP, "SLEEP", &Animal::sleep);
registerCommand(index++, CMD_MAKENOISE, "MAKE NOISE", &Animal::makeNoise);
assert(index == 3);
}
// register using an index rather than code so that we don't overload the meaning of code.
void registerCommand(unsigned int index, unsigned int code, char const* action, animalFunc fn) {
assert(code < COMMAND_COUNT);
_commands[index].code = code;
_commands[index].name = action;
_commands[index].handler = fn;
#if TRACING
Serial.print( "Registering: Index=");
Serial.print(index);
Serial.print(F(", Code="));
Serial.print(_commands[index].code);
Serial.print(F(", Action="));
Serial.println(_commands[index].name);
#endif
}
void report(unsigned int code, char const* msg) {
commandInfo info = getCommandInfo(code);
#if TRACING
Serial.print(msg);
Serial.print(info.code);
Serial.print(" [");
Serial.print(info.name);
Serial.println("]");
#endif
}
void exec(int code, int value) {
commandInfo info = getCommandInfo(code);
#if TRACING
report(code, "Executing: ");
#endif
(this->*info.handler)(value);
}
char const* getName() {
return _name;
}
// base class methods
virtual void eat(int times) {
while (times-- > 0) {
reportDoFunc(CMD_EAT);
}
}
virtual void sleep(int times) {
while (times-- > 0) {
reportDoFunc(CMD_SLEEP);
}
}
virtual void makeNoise(int times) {
while (times-- > 0) {
reportDoFunc(CMD_MAKENOISE);
}
}
virtual void showActions() {
#if TRACING
Serial.print(getName());
Serial.print(F(" Implements "));
Serial.print(COMMAND_COUNT);
Serial.println(F(" actions:"));
for (int i = 0; i < COMMAND_COUNT; i++) {
showAction(i);
}
#endif
}
void showAction(unsigned int index) {
#if TRACING
Serial.print(F("Index: ["));
Serial.print(index);
Serial.print(F("] code: ["));
Serial.print(_commands[index].code);
Serial.print(F("] "));
Serial.println(_commands[index].name);
#endif
}
private:
char const* _name;
// Define an array of pointers to action functions
commandInfo _commands[COMMAND_COUNT];
// return the command associated with code
commandInfo getCommandInfo(unsigned int code) {
#if TRACING
Serial.print(F("IN getCommandInfo: for code="));
Serial.print(code);
Serial.println(F(":"));
#endif
for (int i = 0; i < COMMAND_COUNT; i++) {
#if TRACING
Serial.print(F("Checking Index: ["));
Serial.print(i);
Serial.print(F("], Code: ["));
Serial.print(_commands[i].code);
#endif
if (code == _commands[i].code) {
#if TRACING
Serial.print(F("] FOUND "));
Serial.println(_commands[i].name);
#endif
return _commands[i];
}
#if TRACING
Serial.println("]");
#endif
}
// Invalid command code for this object
assert(false);
}
void reportDoFunc(unsigned int code) {
commandInfo info = getCommandInfo(code);
#if TRACING
Serial.print(code);
Serial.print(F(": "));
Serial.println(info.name);
#endif
}
};
//class Mammal : public Animal { <-- THIS FORM FAILS: "No matching function call to Animal::Animal()
class Mammal {
public:
enum {
CMD_BIRTHS_BABY = 1,
COMMAND_COUNT
};
typedef void(Mammal::*mammalFunc)(int); // <-- Only difference from animalFunc is class name
struct commandInfo {
unsigned int code;
char const* name;
mammalFunc handler; // <-- Only difference from parent class is mammalFunc
};
// Constructor
Mammal(char const* name) : _name(name) { // <-- Identical to parent (Animal) class
registerCommands();
}
// Register all commands supported by this class
virtual void registerCommands() {
#if TRACING
Serial.print(F("Registering "));
Serial.print(getName());
Serial.println(F(" Actions:"));
#endif
// CAN THIS LIST BE ABSTRACTED INTO A TABLE SO REGISTER COMMAND CAN INHERIT?
unsigned int index = 0;
registerCommand(index++, CMD_BIRTHS_BABY, "BIRTHS BABY", &Mammal::birthsBaby);
}
// Only difference from Animal::registerCommand is last argument (mannalFunc instead of animalFunc)
void registerCommand(unsigned int index, unsigned int code, char const* action, mammalFunc fn) {
// Assure that we don't have more commands than we thought...
assert(index < COMMAND_COUNT);
// Update values of current placeholder struct's values
_commands[index].code = code;
_commands[index].name = action;
_commands[index].handler = fn;
#if TRACING
report(code, "Registering: ");
#endif
}
// This is identical to Animal::exec
void exec(int code, int value) {
commandInfo info = getCommandInfo(code);
#if TRACING
report(code, "Executing: ");
#endif
(this->*info.handler)(value);
}
// This is identical to Animal::report
void report(unsigned int code, char const* msg) {
commandInfo info = getCommandInfo(code);
#if TRACING
Serial.print(msg);
Serial.print(info.code);
Serial.print(" [");
Serial.print(info.name);
Serial.println("]");
#endif
}
// This is identical to Animal::getName
char const* getName() {
return _name;
}
// This is identical to Animal::showActions
virtual void showActions() {
#if TRACING
Serial.print(getName());
Serial.print(F(" Implements "));
Serial.print(COMMAND_COUNT);
Serial.println(F(" actions:"));
for (int i = 0; i < COMMAND_COUNT; i++) {
showAction(i);
}
#endif
}
// This is identical to Animal::showAction
void showAction(unsigned int index) {
assert(index < COMMAND_COUNT);
#if TRACING
Serial.print(F("["));
Serial.print(_commands[index].code);
Serial.print(F("] "));
Serial.println(_commands[index].name);
#endif
}
// Concrete Action
virtual void birthsBaby(int times) {
while (times-- > 0) {
reportDoFunc(CMD_BIRTHS_BABY);
}
}
private:
// Inherit?
char const* _name;
// commandInfo is Mammal-specific. Can it be generic?
// Define an array of pointers to action functions
commandInfo _commands[COMMAND_COUNT];
commandInfo getCommandInfo(unsigned int code) {
#if TRACING
Serial.print(F("IN getCommandInfo: for code="));
Serial.print(code);
Serial.println(F(":"));
#endif
for (int i = 0; i < COMMAND_COUNT; i++) {
#if TRACING
Serial.print(F("Checking Index: ["));
Serial.print(i);
Serial.print(F("], Code: ["));
Serial.print(_commands[i].code);
#endif
if (code == _commands[i].code) {
#if TRACING
Serial.print(F("] FOUND "));
Serial.println(_commands[i].name);
#endif
return _commands[i];
}
#if TRACING
Serial.println(F("]"));
#endif
}
// Invalid command code for this object
assert(false);
}
// This is identical to Animal::reportDoFunc
void reportDoFunc(unsigned int code) {
commandInfo info = getCommandInfo(code);
#if TRACING
Serial.print(code);
Serial.print(F(": "));
Serial.println(info.name);
#endif
}
};
int main() {
Animal *pAnimal = new Animal("ANIMAL");
pAnimal->exec(Animal::CMD_EAT, 1);
pAnimal->exec(Animal::CMD_SLEEP, 1);
pAnimal->exec(Animal::CMD_MAKENOISE, 2);
pAnimal->showActions();
Mammal* pMammal = new Mammal("MAMMAL");
pMammal->exec(Mammal::CMD_BIRTHS_BABY, 2);
pMammal->showActions();
pMammal->exec(Animal::CMD_EAT, 2);
delete pMammal;
delete pAnimal;
}