【问题标题】:std::map inizialitazion (only one time)std::map 初始化(仅一次)
【发布时间】:2010-01-25 22:11:55
【问题描述】:

我有一个使用 std::map 转换数据的函数

struct HistoParameter
{
  int nbins;
  float first;
  float last;
  HistoParameter(int _nbins, int _first, int _last) :
    nbins(_nbins), first(_first), last(_last) {};
};

HistoParameter* variable_to_parameter(char* var_name)
{
  std::map<const std::string, HistoParameter*> hp;
  hp[std::string("ph_pt")] = new HistoParameter(100,0,22000);
  hp[std::string("ph_eta")] = new HistoParameter(100,-3,3);
  // ...
  return hp[var_name];
}

我的结构很轻,但图像可能很重。问题是每次我调用这个函数时,它都会创建很多 HistoParameter 对象,也许一个 switch case 更有效。第一个问题:我在制造垃圾?

第二种解决方案:

bool first_time = true;
HistoParameter* variable_to_parameter(char* var_name)
{
  static std::map<const std::string, HistoParameter*> hp;
  if (first_time)
    {
  hp[std::string("ph_pt")] = new HistoParameter(100,0,22000);
  hp[std::string("ph_eta")] = new HistoParameter(100,-3,3);
  // ...
    }
  first_time = false;
  return hp[var_name];

还好吗?更好的解决方案?

【问题讨论】:

    标签: c++ map std garbage


    【解决方案1】:

    第二个解决方案对我来说似乎没问题 - 你可以说:

    if ( hp.empty() ) {
       // populate map
    }
    

    我还会考虑将其设为值映射而不是指针 - 我认为您不需要在这里进行动态分配:

     std::map <std::string, HistoParameter> hp;
    

    然后:

     hp["ph_pt"] = HistoParameter(100,0,22000);
    

    请注意,您不需要显式的 std::string 转换。或者更好:

     hp.insert( std::make_pair( "ph_pt", HistoParameter(100,0,22000 )));
    

    【讨论】:

    • 嘿,同样的想法,但你在 make_pair 上击败了我。忘了它不仅仅是 make_tuple 在那里-_-。
    • 我喜欢 ht.empty() 因为我可以不使用全局变量。我使用指针是因为将来这个类会变得非常不同和更重。
    • 你可以在必要时进行更改——这是使用封装的主要原因之一。就目前而言,如果您不使用智能指针,您会遇到一些资源泄漏问题。由于地图是静态的,这些可能无关紧要,但仍然......
    • @wiso,您仍然可以传递/返回指向地图中类的指针。指针可以用于非动态分配的对象。
    • @Kornel Kisielewicz,如果你指的是 boost::make_tuple,你也可以在这里使用 make_tuple,它只是一个替代品。
    【解决方案2】:

    第一个解决方案会产生大量垃圾。为什么不按值返回类?它非常轻量级,您不必动态分配它。

    HistoParameter variable_to_parameter(char* var_name)
    {
      static std::map<const std::string, HistoParameter> hp;
      if ( hp.empty() )
      {
        hp.insert( std::make_pair( "ph_pt", HistoParameter(100,0,22000) ) );
        hp.insert( std::make_pair( "ph_eta", HistoParameter(100,-3,3) ) );
      //...
      }
      return hp[var_name];
    }
    

    如果返回的类变得更大,并且您想要一个电动工具,那么试试boost::flyweight

    如果你不想传回一个大的结构,你可以这样做:

    HistoParameter& variable_to_parameter(char* var_name)
    {
      // same code
    }
    

    ...如果您希望它不可变,甚至可以输入 const

    编辑:按照 Niel 的建议添加了 make_pair。

    【讨论】:

      【解决方案3】:

      您的第二个解决方案肯定会提高效率,但(至少在 IMO)可能不是最好的实现。首先,它使first_time 公开可见,即使只有variable_to_parameter 真正关心它。您已经在函数中将hp 设为静态变量,first_time 也应该如此。

      其次,我不会对 HistoParameter 值使用指针和/或动态分配。在一个 int 和两个 float 处,根本没有理由这样做。如果你真的传递它们太多以至于复制成为一个问题,你可能最好使用某种智能指针类而不是原始指针——后者更难使用,也更难制作异常安全。

      第三,我会考虑是否值得将 variable_to_parameter 变成仿函数而不是函数。在这种情况下,您将在 ctor 中初始化映射,因此您不必在每次调用 operator() 时检查它是否已初始化。您也可以将两者结合起来,方法是在函子中有一个静态映射。如果 ctor 不存在,则 ctor 对其进行初始化,并且 operator() 只是进行查找。

      最后,我注意到map::operator[] 主要用于插入项目——如果它不存在,它会使用指定的键创建一个项目,但是当你在寻找一个项目时,你通常不会'不想创建一个项目。为此,您通常最好改用map.find()

      【讨论】:

        【解决方案4】:

        我会有一个 std::map 成员并做

        InitializeHistoParameter() 
        {
           myMap["ph_pt"] = new ...
           myMap["ph_eta"] = new ...
        }
        

        然后

        HistoParameter* variable_to_parameter(char* var_name) 
        {
            return myMap[var_name];
        }
        

        【讨论】:

          【解决方案5】:

          无论哪种方式,您都会造成内存泄漏。 每次调用= 运算符,例如:

          hp[std::string("ph_pt")] = new HistoParameter(100,0,22000);
          

          您正在创建一个新的 HistoParameter 对象,并将键“ph”与这个最近的对象配对,使前一个对象悬空。 如果每次创建一个新对象是您的实际意图,您可能需要调用

          delete hp[std::string("ph_pt")]; 
          

          new 操作之前。

          我的建议是尽可能避免原始的new 操作,并诉诸诸如boost::share_ptr 之类的智能指针来进行对象生命周期管理。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-07-30
            • 1970-01-01
            • 2012-12-05
            • 1970-01-01
            • 2017-05-02
            相关资源
            最近更新 更多