2013-09-26 39 views
4

我需要一種方法來實例化基於它的類名傳遞的對象爲std :: string。這是工作的權利,但需要加以概括:字符串名稱的C++通用對象工廠

void* create(std::string name) { 
    if(name == "classOne") return new ClassOne(); 
    else if(name == "classTwo") return new ClassTwo(); 
    /* ... */ 
} 

什麼我沒有

  • 控制的類實例化:可以滿三十歲派對類。這個類沒有變化(即基礎祖先,多態創造者方法等)。
  • 完整的類名列表:更多的類可以稍後添加,並且不應該改變這個工廠。
  • 圍繞要實例化的類的包裝器:作爲前兩點的結果。

其他任何事情都是一回事。

最好使用情況將是:

int main() { 
    void *obj = create("classTree"); // create object based on the string name 
    /* ... */ 
    // once we know by context which specific class we are dealing with 
    ClassTree *ct = (ClassTree*)obj; // cast to appropiate class 
    std::cout << ct->getSomeText() << std::endl; // use object 
} 

作爲一個方面,也許不相干的音符,您考慮被實例化可能來自一個類或結構的對象。

添加的信息

我看到的是需要更多的上下文。這裏是我的具體使用情況,簡化爲:

// registration mechanism 
int main() { 
    std::map< std::string, void(*func)(std::string, void*) > processors; // map of processors by class name 
    processors["ClassFour"] = (void(*)(std::string, void*)) &classFourMessageProcessor; // register processor (cast needed from specific to generic) 
} 
// function receiving string messages 
void externalMessageHandler(std::string msg) { 
    std::string objType = extractTypeFromMessageHeader(msg); // extract type from message 
    // now that we know what we are dealing with, create the specific object 
    void *obj = create(objType); // << creator needed 
    processors[objType](msg, obj); // dispatch message to process 
} 
// previously registered message processor 
void classFourMessageProcessor(std::String msg, ClassFour *obj) { 
    std::string streetAddress = msg.substr(10, 15); // knowing the kind of message we can extract information 
    obj->moveTheEtherTo(streetAddress); // use the created object 
} 

添加的信息

我使用C++ 11的最新的GNU編譯器。

+1

不僅僅是在我們知道我們正在處理的具體類是什麼時才實例化正確的類,然後做那些你會用這個'void *'做的事情? – Kal

+1

@Kal在知道它是什麼類之前,您可能需要使用'obj'。 – SirGuy

+1

這不是創建工廠的正常方式。由於大的if語句塊,它不會很好地擴展。請參閱http://stackoverflow.com/questions/5120768/how-to-implement-the-factory-pattern-in-c-correctly – Bathsheba

回答

2

您可以爲每個類類型只存儲一個工廠函數。一種簡單的方法是使用一個模板

template <typename T> 
void* creator() { 
    return new T(); 
} 

和存儲那些在地圖以及(即「ClassFour」鏈接到creator<ClassFour>ClassFourMessageProcessor)。

編輯:澄清,processors成爲

typedef void* (*CreatorFunc)(); 
typedef void (*ProcessorFunc)(std::string, void*); 

typedef std::pair<CreatorFunc, ProcessorFunc> Entry; 
std::map< std::string, Entry > processors; 

添加一個新的類很簡單,只要

processors["SomeClass"] = Entry(creator<SomeClass>, ClassFourMessageProcessor); 
+0

不確定此用途的用途。這比「新的ClassOne()」更容易嗎? – tadman

+0

這不符合他關於修改的要求。每個新班級都會有這種方法的要求。 –

+0

它允許工廠也保存在列表中,從而使創建函數獨立於已註冊的類。它避免了所有支持的類類型的手寫分支。這也是一個非常標準的模式。 –

-1

這裏有一個觀點:

  1. 對於每個類,創建createInsrance()函數(不是方法)實例化一個實例並返回一個指向void *的指針。注意這個函數不是類的一部分 - 只是一個普通的函數。
  2. 維護一個字符串映射到函數指針createInstance類型函數的映射。
  3. 在地圖中「註冊」每個相關類 - 將字符串函數指針對添加到地圖。
  4. 現在,通用創建將在映射中搜索字符串並調用特定的createInstane,返回新實例的ptr。

現在您不對類進行任何更改,並且可以在不重新編程工廠的情況下添加更多類。 你可能至少把#1作爲模板 - 一定要讓編譯器實例化具體的實現。

+0

應該可以工作,但它不像我想要的那樣通用。我正在尋找一種方法來做到這一點,然後忘掉這件事。不管怎麼說,還是要謝謝你。 – yiown

+0

假設您希望能夠在更高版本中添加新類,您如何期望編譯器/運行時代碼「知道」正在添加的新類的「名稱」? –

+0

在這種情況下,必須爲每個類定義一個函數的負擔過重,相反,使編譯器這樣做的通用方法更可取。 – yiown

0

也許下面的問題與lokup表將是一個不錯的解決方案。

(注:我不知道您使用的編譯器至極,所以這個解決方案是C++ 03,你可以採取unordered_map而不是地圖,如果你使用的是帶有C++ 11的支持編譯)

(注2:你可以使用智能指針過,並採取收益值的照顧,白衣這個例子,我只是想表現出的形式給出)

#include <iostream> 
#include <string> 
#include <map> 
#include <vector> 


struct ClassMaker 
{ 
    virtual void* getInstance() const = 0; 
    virtual ~ClassMaker() {} 
}; 

class Factory 
{ 
private: 
    std::map<std::string, ClassMaker*> lokupTable; 
    typedef typename std::map<std::string, ClassMaker*>::iterator Iterator; 
public: 
    void addClass(const std::string& key, ClassMaker* const newClassMaker) 
    { 
     lokupTable[key] = newClassMaker; 
    } 

    void* create(const std::string& key) 
    { 
     void* result = NULL; 
     Iterator it = lokupTable.find(key); 

     if(it != lokupTable.end()) 
      result = (it->second)->getInstance(); 
     return result; 
    } 

    void releaseTable() 
    { 
     for (Iterator it = lokupTable.begin(); it != lokupTable.end(); ++it) 
      delete it->second; 
    } 
}; 

struct IntCreator : public ClassMaker 
{ 
    void* getInstance() const 
    { 
     return new int; 
    } 

}; 

struct StringCreator : public ClassMaker 
{ 
    void* getInstance() const 
    { 
     return new std::string; 
    } 

}; 



int main() 
{ 
    Factory myFactory; 
    myFactory.addClass("int", new IntCreator); 
    myFactory.addClass("string", new StringCreator); 

    int* myInt = reinterpret_cast<int*>(myFactory.create("int")); 
    *myInt = 10; 
    std::cout<< *myInt << std::endl; 
    delete myInt; 
    myFactory.releaseTable(); 
    return 0; 
} 
+0

請閱讀問題描述。這不符合它。謝謝 – yiown

+0

你的「沒有要點」之一是:「全班名單列表:更多的班級可以稍後添加,不應該改變這個工廠。」有了這個辦法,工廠永遠不會改變,你可以用一種方法添加新的類。 – hidrargyro

+0

它與我所描述的方法相同,除了它沒有爲工廠模板,而是使用整個多態類而不是單個工廠函數(它由於它需要對其創建的實例進行內存管理,因此使事情複雜化)。據我的理解,這也符合規範,但。 –

-1

你可以考慮Boost.MPL?與STL不同,它允許創建包含類型的容器,而不是實例。從字符串到類型的映射會給你想要的工廠,不是嗎?