2013-07-10 51 views
1

我有一個Parent抽象類,並推導出FooBar類。他們都有一個構造函數,參數爲string name。它們有一個字符串類型()成員函數,它分別返回「foo」和「bar」。C++:指向「新類()」函數的指針

現在我想用鍵「foo」和「bar」構建一個dictionnary,其值等於指向創建相應類的新實例的函數的指針。這意味着這些指針會Parent* (*) (const string &)型和執行他們的將是等效於調用和new Bar(name)

我能避免爲每個類,將只需要調用新的,並得到一個指向該靜態函數創建一個靜態函數?

謝謝!

編輯:其實,其背後的目標是在我的應用中實現一個插件系統,插件添加了新的派生類類型。插件作者通常是我的客戶,因此,我想讓他們擁有最簡單優雅的界面。因此,聲明和註冊工廠功能應該儘可能簡單且儘可能短

+0

您可以通過創建一個非靜態函數來避免創建一個靜態函數。 – juanchopanza

+0

@juabchopanza:呵?這是一個分配器函數,所以它不應該在類實例上工作(與克隆函數相反)...主題 – galinette

+0

正好,我不是說它應該在類實例上工作。 – juanchopanza

回答

1

您需要爲您希望能夠通過這種方式構建的每個類實例化工廠函數。下面的代碼展示瞭如何做到這一點,而不必再發生靜態成員和不能用手寫了許多不同的功能:

#include <iostream> 
#include <map> 
#include <functional> 
#include <string> 
#include <memory> 

struct Parent{ 
    Parent(std::string){} 
}; 

struct Child1 : public Parent{ 
    Child1(std::string d) : Parent(d){ 
     std::cout << "Child1 constructor: " << d << std::endl; 
    } 
}; 

struct Child2 : public Parent{ 
    Child2(std::string d) : Parent(d){ 
     std::cout << "Child2 constructor: " << d << std::endl; 
    } 
}; 

template <typename Product, typename Return, typename Parameter> 
Return factory(const Parameter& a){ 
    return Return(new Product(a)); 
} 

std::map<std::string, std::function<Parent*(std::string)> > mrp; 
std::map<std::string, std::function<std::shared_ptr<Parent>(std::string)> > msp; 

int main(){ 
    mrp["Child1"] = &factory<Child1, Parent*, std::string>; 
    mrp["Child2"] = &factory<Child2, Parent*, std::string>; 

    Parent* a = mrp["Child1"]("one"); 
    delete a; 
    std::unique_ptr<Parent> b(mrp["Child2"]("two")); 


    msp["Child1"] = &factory<Child1, std::shared_ptr<Parent>, std::string>; 
    msp["Child2"] = &factory<Child2, std::shared_ptr<Parent>, std::string>; 

    msp["Child1"]("one"); 
    msp["Child2"]("two"); 
} 

試試這個代碼here。 此外,你可以看到,這種方法可以「配置」使用std :: shared_ptr的,裸指針,......和不同所有制semanthics,注意行:

std::unique_ptr<Parent> b(mrp["Child2"]("two")); 

然而,任何變化你在問題中簡要描述的是,它的一個變種!你所要做的是一個抽象工廠,「標準」實現完全依賴於爲你想要構建的每個類創建一個工廠函數。正如我所說的,它不需要是類的靜態成員,它可以是非成員的非友元函數,但這並沒有太大的改變(除了更好的封裝,以及構造函數的存在公衆或工廠成爲朋友,這在某種程度上打破封裝)。

對象工廠在loki中以非常優雅的方式實現。請參閱Modern C++(Alexandrescu),詳細討論設計模式本身以及Loki給出的實現。

至於你的編輯:在洛基註冊是甜而簡單的(引自書):

// Implementation module for class Line 
// Create an anonymous namespace 
// to make the function invisible from other modules 
namespace 
{ 
Shape* CreateLine() 
{ 
return new Line; 
} 
// The ID of class Line 
const int LINE = 1; 
// Assume TheShapeFactory is a singleton factory 
// (see Chapter 6) 
const bool registered = 
TheShapeFactory::Instance().RegisterShape( 
LINE, CreateLine); 
} 
+0

@DyP實際上,您可以通過爲每個類編寫一個*非靜態*函數來輕鬆滿足要求:-) – juanchopanza

+0

@juanchopanza但是這需要您可以從類的字典中創建虛擬實例。考慮到類不變量,const成員,ref成員,RAII等等,這可能很困難。對吧? – dyp

+0

@DyP爲什麼你不能創建一個對象並返回它?無論如何,我並不是在暗示這樣做,只是指出需求是混淆的。 – juanchopanza

4

你描述的是一個工廠方法模式。 這裏有一個鏈接:http://en.wikipedia.org/wiki/Factory_method_pattern 靜態函數或某種工廠基本上是你想要的東西。

爲什麼要避免靜態創建方法?

+0

嗨, 感謝您的鏈接。實際的代碼更加複雜,並且是一個插件系統,插件將新的派生類類型添加到應用程序中。我希望插件作者儘可能少編寫代碼(他們是客戶......),儘可能多地包含在頭文件中。 – galinette

8

我可以避免爲每個只需調用new的類創建靜態函數,並獲取指向此靜態函數的指針嗎?

是的,你可以創建一個通用的功能:

template <typename T> 
ptr_type<Parent> create() { 
    return new T(); 
} 

...然後取它的地址(但你需要獲得一個地址每派生類型)。

請注意,我有明確使用T*作爲返回類型 - 原始指針不應該自己的記憶......一個合適的智能指針應改爲在這裏使用。

+0

您確實應該使用ptr_type ,否則您將無法將它們放入字典中。 – riv

+0

我仍然無法看到如何在字典中添加指向模板函數的指針。 –

+0

@WouterHuysentruit'&創建'和'&創建'。就像一個指向任何其他函數的指針。(當然,它們需要相同的返回類型) –