2014-06-05 53 views
1

我有一个非常简单的C++查找表调度命令:子类查找表

template <class T> Action* CreateAction(Command *c) 
{ 
    return new T(c); 
} 

typedef Action* CreateActionFunc(Command *c); 

typedef struct ActionTable { 
    string name; 
    CreateActionFunc *func; 
} ActionTableEntry; 

vector<ActionTableEntry> GlobalActionTable = { 

    { "quit"  , &CreateAction<DoQuit> }, 
}; 

这工作得很好,但我宁愿让我的CreateAction功能构建堆栈中的新对象,并通过值返回。但是当我写这个:

template <class T> T CreateAction(Command *c) 
{ 
    return T(c); 
} 

typedef Action CreateActionFunc(Command *c); 

然后程序将不再编译。首先,我得到一个错误,即一个抽象类无法实例化(在typedef行上),并且还有一个错误,即该表的初始化列表与该向量的类型不匹配。

有一个非常类似的问题here但每个答案在工厂方法中使用new,这是明确我想要避免的。如何才能做到这一点?

回答

1

您不能按值对象使用多态性。 需要指针或引用。 我猜在这里你有一个Action接口(所以抽象类),所以你不能创建这个动态类型的对象。你所能做的就是发送一个类型为Action的指针,它带有一个动态类型的派生类(所以你已经在做我已经做过的事情)。 您可以在堆栈上创建派生类型的值对象,并返回Base类上的引用并仍使用多态,但是您需要解决Derived对象问题的生命周期。

+0

感谢Kiroxas,我想这回答了这个问题! – Segfault

1

Action子类比Action类本身具有更多信息 - 指向其成员函数,数据成员等的表的指针。如果按值返回,则没有足够的内存来容纳此信息。会发生称为切片的事情。

This答案解释得更好。


如何做这样的事情,而不是:

class Action { 
    void do_something(Command& params) = 0; 
}; 
class SayHello { 
    void do_something(Command& params) { std::cout << "Hi!" << std::endl; } 
} 
class SayBye { 
    void do_something(Command& params) { std::cout << "Goodbye." << std::endl; } 
} 

..... 

SayHello hello; 
SayBye bye; 
Quit  quit; 
std::map<string, Action&> action_table = { 
    {"hello", hello}, 
    {"bye", bye}, 
    {"quit", quit}, 
}; 

.... 

Action& getAction(Command* command) { 
    ...; 
    return action_from_map; 
} 

这就造成了一次行动,并通过引用返回它们。

+0

是的,这是有道理的。我真的不想返回(抽象)Action类型,我想返回具体子类之一的实例....但是,如何正确地编写查找表来执行此操作?我可以使用lambdas或函子吗?是否有一个函数指针的typedef语法可以指向模板函数的任何特化? – Segfault

+1

你不能有一个指向任何模板专业化的函数指针。必须指定模板类型。 – excray

+0

@excray哪个模板?我删除了所有模板。我认为在这种情况下继承是可行的。 – nishantjr

1

那么简单的事情呢?

std::map<string, std::function<void(CommandArgs const&)>> action_table = 
{ 
    {"hello", [](CommandArgs const& args) { /* do something */ }}, 
};