2016-04-13 55 views
3

我已經寫了一類具有下列靜態方法:C++函數與在文件範圍內使用的副作用,訪問單

MyMap& Manager::GetMap(void) 
{ 
    static MyMap* factories = new MyMap(); 

    return (*factories); 
} 

其中「MyMap中」是一個typedef:

unordered_map<string, function<Base* (Dependency& d)>> 

也有從基地派生的各種類型,例如

class Derived1 : public Base 
{ 
public: 
    Derived1(Dependency& d); 
}; 

請考慮以下用法。

我定義爲Derived1實現文件如下:

#include "Derived1.h" 
#include "Manager.h" 

int RegisterDerived1(void) 
{ 
    Manager::GetMap()["Test"] = [](Dependency& d){ return new Derived1(d); }; 

    return 0; 
} 

int Reg = RegisterDerived1(); 

你不能說在文件範圍內的功能,但是你可以一個函數的返回值賦給一個全局變量,即使該功能有副作用。因此,在使用「管理器」時,「MyMap」將包含用於各種派生類型的「基本」(到目前爲止)的字符串/函數對。意圖是新的派生類型的「基礎」註冊自己與「經理」,能夠構建該類型的實例,並根據名稱選擇哪種類型。

我想知道如果這代表安全的行爲和/或是否有替代實現,以獲得預期的效果?

我已經意識到在這篇文章中提出了一種通用的登記對象,需要在其構造上述一對並執行登記的,一個靜態實例,其中被定義爲要被註冊的每一個類。

http://accu.org/index.php/journals/597

+0

看來,你基本上是問「延遲初始化」和其他相關的代碼的東西都沒有太大的相關性。你覺得,這篇文章回答你的Q:[單身模式懶惰初始化](http://stackoverflow.com/q/21252296/514235)。如果是,那麼它可以作爲重複關閉。 – iammilind

+0

@iammilind我很清楚單個實現的行爲,這是我認爲這個問題的關鍵。我只是包含了其他位的上下文。我不太清楚的是在文件範圍使用上述自由函數的含義。這感覺就像一件壞事,因爲我需要解決一個限制。 –

回答

1

原則是好的。

你可能要考慮一些事情:

  1. 返回原始指針是一個壞主意 - 使用的unique_ptr來代替。

  2. 你真正想要的依賴&參考是非const的?

  3. 隱藏內部實現。用戶不需要知道(或關心)它是無序的映射。

稍微修改其內部註釋的版本,你要考慮:

#include <functional> 
#include <unordered_map> 
#include <memory> 
#include <string> 

struct Base 
{ 
    virtual ~Base() = default; 
}; 

struct Dependency 
{ 

}; 

struct Manager 
{ 
    // I notice that Depdendency& is not const. Was that what you wanted? 
    using factory_function = std::function<std::unique_ptr<Base> (Dependency& d)>; 

    // public registration function hides internal implementation of map 
    static bool register_function(const std::string ident, factory_function f) 
    { 
    return GetMap().emplace(std::move(ident), std::move(f)).second; 
    } 

    // public create function hides internal implementation of map 
    // returns a unique_ptr - much better! 
    static std::unique_ptr<Base> create(const std::string& ident, Dependency& d) 
    { 
    // this will throw an exception if the factory does not exist. 
    // another implementation could substitute a known version of Base, 
    // for example. But now it's under your control and the user does 
    // not have to think about it. 
    return GetMap().at(ident)(d); 
    } 

    private: 

    using MyMap = std::unordered_map<std::string, factory_function>; 

    // private map implementation. In future we may want to add a mutex 
    // (in case the map can be dynamically updated?) 
    // so let's encapsulate 
    static MyMap& GetMap() 
    { 
    // no need for new here. Static variables are cleanly destructed at 
    // the end of the program, and initialised the first time the code 
    // flows over them. 
    static MyMap _map; 
    return _map; 
    } 
}; 

struct Derived1 : Base 
{ 
    Derived1(Dependency&) {} 
}; 

// now we don't need to care about Manager's implementation. 
// this is better - we are decoupled. 
bool derived1_registered = Manager::register_function("Derived1", 
                [](Dependency& d) 
                { 
                 return std::make_unique<Derived1>(d); 
                }); 

int main() 
{ 
    Dependency d; 
    auto p = Manager::create("Derived1", d); 

    return 0; 
} 
+0

是的,我對這個Dependency類感興趣的非常量方法。不過,第1和第3點是很好的。有關以這種方式使用免費功能的想法? –

+1

免費功能絕對沒問題。如果你想要,你甚至可以模板化,所以你只需要寫一次。 –