在文件范围内使用具有副作用的C++函数,访问singleton



我用以下静态方法编写了一个类:

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

其中"MyMap"是的typedef

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

也有多种类型衍生自Base,例如

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();

不能在文件范围内调用函数,但可以将函数的返回值分配给全局变量,即使该函数有副作用。因此,当"Manager"使用时,"MyMap"将包含各种派生类型的"Base"的字符串/函数对(到目前为止)。其目的是让"Base"的新派生类型向"Manager"注册,能够构造该类型的实例并根据名称选择类型。

我想知道这是否代表了安全行为和/或是否有其他实现方式可以达到预期效果?

我已经了解了这篇文章,它提出了一个通用的注册对象,该对象在其构造函数中采用上述对并进行注册,然后为要注册的每个类定义一个静态实例。

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

原理很好。

你可能需要考虑的几件事:

  1. 返回原始指针是个坏主意-请改用uniqueptr。

  2. 你真的想要依赖关系&引用为非常量?

  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;
}

最新更新