2012-05-08 67 views
1

在線搜索了工廠模式的不同方法後,我實現了自己的版本,對此我非常滿意。 Register成員函數爲模板類型創建一個函數指針,將其存儲在使用索引作爲鍵的std :: map中。這下面的代碼編譯和運行沒有麻煩(Windows 7 64位Code :: Blocks 10.05與GCC)。調用模板化成員函數與模板化全局函數在通用工廠中創建對象

#ifndef OBJECT_FACTORY_HPP 
#define OBJECT_FACTORY_HPP 

#include <map> 

namespace internal { 
    template<typename BaseType, typename ObjectType> 
    BaseType* CreateFunction() { 
     return new ObjectType; 
    } 
} 

template<typename BaseType, typename IndexType> 
class ObjectFactory { 
    public: 
     ObjectFactory(); 

     template<typename ObjectType> 
     bool Register(const IndexType& index); 

     bool Unregister(const IndexType& index); 

     BaseType* Create(const IndexType& index); 

    private: 
     typedef BaseType* (*creationCallback)(); 
     typedef std::map<IndexType, creationCallback> Registry; 
     Registry registry; 

// private: 
//  template<typename ObjectType> 
//  BaseType* CreateFunction(); 
}; 

template<typename BaseType, typename IndexType> 
ObjectFactory<BaseType, IndexType>::ObjectFactory() { 
    registry.clear(); 
} 

template<typename BaseType, typename IndexType> 
template<typename ObjectType> 
bool ObjectFactory<BaseType, IndexType>::Register(const IndexType& index) { 
    if (registry.find(index) != registry.end()) 
     return false; 

    registry[index] = &internal::CreateFunction<BaseType, ObjectType>; 
    // registry[index] = &CreateFunction<ObjectType>; <-- FAILS! 
    return true; 
} 

template<typename BaseType, typename IndexType> 
bool ObjectFactory<BaseType, IndexType>::Unregister(const IndexType& type) { 
    if (registry.find(type) == registry.end()) 
     return false; 

    return (registry.erase(type) == 1); 
} 

template<typename BaseType, typename IndexType> 
BaseType* ObjectFactory<BaseType, IndexType>::Create(const IndexType& index) { 
    if (registry.find(index) == registry.end()) 
     return NULL; 

    return registry[index](); 
} 

//template<typename BaseType, typename IndexType> 
//template<typename ObjectType> 
//BaseType* ObjectFactory<BaseType, IndexType>::CreateFunction() { 
// return new ObjectType(); 
//} 

#endif 

我原來的做法是有CreateFunction作爲私有成員從用戶隱藏(看註釋的部分,請注意非成員函數有一個額外的模板參數)。然而,這種失敗與以下錯誤消息,都指向哪裏函數指針存儲在註冊成員函數行:

In member function 'bool ObjectFactory<BaseType, IndexType>::Register(const IndexType&) [with ObjectType = Triangle, BaseType = Shape, IndexType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]':| 
instantiated from here 
error: no matches converting function 'CreateFunction' to type 'class Shape* (*)()'| 
error: candidates are: template<class ObjectType> BaseType* ObjectFactory::CreateFunction() [with ObjectType = ObjectType, BaseType = Shape, IndexType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]| 

我寫了一個小測試客戶端來進行測試:

#include "ObjectFactory.hpp" 
#include <iostream> 

class Shape { 
    public: 
     Shape() {} 
     virtual void print() { std::cout << "At heart, I'm a shape"; } 
}; 

class Triangle : public Shape { 
    public: 
     Triangle() {} 
     void print() { Shape::print(); std::cout << ", but I'm truly a triangle" << std::endl; } 
}; 

int main(int argc, char* argv[]) { 
    ObjectFactory<Shape, std::string> objectFactory; 

    objectFactory.Register<Triangle>("triangle"); 

    Shape* triangle = objectFactory.Create("triangle"); 
    triangle->print(); 

    delete triangle; 

    return 0; 
} 

這甚至可能嗎?我覺得它應該是,我明白,我不知何故調用不正確的成員函數,但我不明白爲什麼。在相關說明中,由於有人可能會提及它,因此我計劃使用* boost :: shared_ptr *作爲分配,而不是簡單的new運算符;)有關實現的任何其他建議或建議也值得歡迎。

+1

'BaseType * CreateFunction(){return new ObjectType; }'應該是'std :: unique_ptr CreateFunction(){std :: unique_ptr p(new ObjectType());返回p; }'。只要對「刪除」說「不」。 –

+0

如果您閱讀我的文章的最後一段,您可以看到我已經計劃使用boost :: shared_ptr。我已經有了我公平的內存泄漏份額,所以我同意你最後的聲明。也提醒我,我應該閱讀std :: unique_ptr,所以謝謝:) – NordCoder

回答

0

的創建功能應該是一個靜態成員:

template<typename ObjectType> static BaseType* CreateFunction(); 

(上面的代碼,而不static,被註釋的在你的例子)。

現在你的其他註釋掉的代碼是有效的:

registry[index] = &CreateFunction<ObjectType>; 

編輯:澄清一些困惑:

這裏的問題主要是在語法。模板和private在這裏並不重要,所以讓我們簡化的情況:

struct ObjectFactory { 
    void CreateFunction() {std::cout << "message";} 
    void AnotherFunction() 
    { 
     ... = &CreateFunction; // what's that? 
    } 
}; 

如果CreateFunction是一個非靜態成員函數,語法&CreateFunction是不正確;我想gcc就是這麼抱怨的。然而,讓事情變得更糟,MS Visual Studio會嘗試通過內部「修正」語法&ObjectFactory::CreateFunction來「幫助」你並嘗試使用它;但它失敗並帶有難以理解的錯誤信息(事實上,你有更多的模板會加劇它們)。

我不確定Visual Studio部分(我記得幾年前,MSVC 6有這樣的麻煩; Visual Studio的新版本沒有這個問題)。

+0

代碼的註釋,因爲它是原來的方法。新的使用在內部命名空間內聲明的全局函數。 – NordCoder

+0

@NordCoder我將你的問題理解爲「我的註釋代碼不起作用;我該如何解決它?」 – anatolyg

+0

對不起,我會嘗試重新說明我的問題。 我只是試了一下,它的工作原理非常完美,非常感謝:)我不明白爲什麼這應該是無效的語法(即使GCC告訴我這是)或爲什麼私人成員函數不應該工作。也許GCC與MS Visual Studio做同樣的事情?將函數指針typedef聲明爲typedef BaseType *(ObjectFactory :: * creationCallback)();相反會產生更復雜的錯誤。 – NordCoder

1

從我們的文件中,我們匿名我們的命名空間:

// namespace internal { 
    namespace   { 
    template<typename BaseType, typename ObjectType> 
    BaseType* CreateFunction() { ... } 
} 

原先寫入的非靜態函數現在可以叫做,沒有命名空間限定:

// registry[index] = &internal::CreateFunction<ObjectType>; 
    registry[index] =   &CreateFunction<ObjectType>; 

文件-scoped CreateFunction函數在翻譯單元以外的代碼中是不可見的,僅在類ObjectFactory中被調用。

這對於問題中提出的CreateFunction(來自ObjectFactory內)的private訪問說明符非常接近。

+0

這也是一個非常好,稍乾淨的方法(儘管你忘了第一個模板參數)。感謝您的建議:) – NordCoder