2010-05-15 126 views
1

我試圖創建一個結構陣列,其連結輸入字符串類別如下:類查找結構陣列++

struct {string command; CommandPath cPath;} cPathLookup[] = { 
    {"set an alarm", AlarmCommandPath}, 
    {"send an email", EmailCommandPath}, 
    {"", NULL} 
}; 

將被使用如下:

CommandPath *cPath = NULL; 
string input; 
getline(cin, input); 
for(int i = 0; cPathLookup[i] != ""; i++) { 
     if(cPathLookup[i].command == input) 
       cPath = new cPathLookup[i].cPath; 
} 

顯然,這段代碼沒有意義,但我認爲我的意圖很明顯 - 根據輸入,我想將cPath初始化爲新的AlarmCommandPath或新的EmailCommandPath。我可以用一個函數根據輸入返回一個實例來處理它,但是整個ifs序列看起來不夠好看。

我還應該注意,如果它不明顯且重要,那麼AlarmCommandPath和EmailCommandPath是從CommandPath派生而來的,而CommandPath是一個抽象類。

感謝您提供任何幫助。

編輯:我只注意到,儘管COMMANDPATH是抽象的,我有一個聲明:

CommandPath *cPath = NULL; 

的工作代碼。爲什麼編譯?

回答

1

AlarmCommandPath和EmailCommandPath是從COmmandPath派生的,對嗎?

在這種情況下,您無法將AlarmCommandPath/EmailCommandPath的實例分配給CommandPath - 這在技術上是可行的,但它不會做到您想要的。實例 CommandPath將保留爲CommandPath的一個實例(它將具有CommandPath的虛函數表),而不管分配給它的是什麼。

您需要使用工廠方法(將返回CommandPath *的函數)。類似的東西:

struct A{ 
}; 

struct B: public A{ 
}; 

struct C: public A{ 
}; 

A* factoryA(){ 
    return new A(); 
} 

A* factoryB(){ 
    return new B(); 
} 

A* factoryC(){ 
    return new C(); 
} 

typedef A* (*FactoryMethod)(); 

struct{ 
    const char* command; 
    FactoryMethod factoryMethod; 
} factoryTable[] = { 
    {"A", factoryA}, 
    {"B", factoryB}, 
    {"C", factoryC}, 
    {0,0} 
}; 
+0

這可以做到沒有堆分配?我的意思是,這些工廠方法可以在堆棧上構造一個A或B或C並按值返回它嗎? – Segfault 2014-06-05 14:29:40

1

您不能在結構中存儲類型,但可以存儲指向創建類型的函數的指針。

CommandPath * CreateEmail() { 
    return new EmailCommandPath; 
} 

CommandPath * CreateAlarm() { 
    return new AlarmCommandPath; 
} 

然後你的結構是這樣的:

typedef Command * (* CreateFunc)(); 
struct MyMap { 
    string command; 
    CreateFunc func; 
}; 

和地圖:

MyMap m[] = {{"email", CreateEmail }, {"alarm", CreateAlarm}}; 

你再看看像以前那樣得到一些索引i,並使用它:

CommandPath * p = m[i].func(): 

你可以創建指針來抽象t ypes - 你不能創建它們的實例。

0

我假設你想如果你的系統\ else語句來實現查表作爲替代到大。

爲了清楚起見,我會選擇使用工廠設計模式。如果在很多地方圍繞代碼重複執行,那麼擁有大量的if/else邏輯將非常糟糕。只要它在一個地方,即工廠,那麼在我看來,你有一個很好的設計。

0

就我個人而言,如果您只有1個工廠爲其接收的字符串的不同值創建不同的「CommandPaths」,我並不認爲這是一個巨大的問題。無論如何,你的代碼不會工作,因爲你不能按照你的方式存儲類型。

如果我必須這樣做,那麼我會使用函數指針指向工廠函數,並使用std :: map將字符串映射到這些函數(如代碼中所示),也許可以將指針包裝在適當的智能而不是終場前,使用原始指針:

#include <string> 
#include <map> 

struct A { 
}; 

struct B : public A { 
}; 

struct C : public A { 
}; 

A *BFactory(){ 
    return new B(); 
} 

A *CFactory(){ 
    return new C(); 
} 

typedef A *(*Factory)(); 
typedef std::pair<std::string,Factory> Pair; 
typedef std::map<std::string,Factory> Map; 

Pair Pairs[] = 
{ 
    std::make_pair("alarm", BFactory), 
    std::make_pair("email", CFactory) 
}; 

Map Lookup(Pairs, Pairs + sizeof(Pairs)/sizeof(*Pairs)); 

A *CreateInstance(const std::string &Type) 
{ 
    Map::const_iterator i = Lookup.find(Type); 

    if(i != Lookup.end()) 
     return i->second(); 
    else 
     return 0; 
} 

至於你提到的指針和抽象類的問題,你可以有一個指向一個抽象類,但不能實例化一個抽象類。