2014-06-05 85 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 */ }}, 
};