【问题标题】:Theoretical clarification regarding maps and iterators关于映射和迭代器的理论说明
【发布时间】:2015-11-19 02:18:19
【问题描述】:

如果我有一个带有地图的类作为私有成员,例如

class MyClass
{
  public:
    MyClass();
    std::map<std::string, std::string> getPlatforms() const;
  private:
    std::map<std::string, std::string> platforms_;
};

MyClass::MyClass()
        :
{
  platforms_["key1"] = "value1";
  // ...
  platforms_["keyN"] = "valueN";
}

std::map<std::string, std::string> getPlatforms() const
{
  return platforms_;
}

在我的主函数中,这两段代码会有区别吗?

代码1:

MyClass myclass();
std::map<std::string, std::string>::iterator definition;
for (definition = myclass.getPlatforms().begin();
     definition != myclass.getPlatforms().end();
     ++definition){
  std::cout << (*definition).first << std::endl;
}

代码2:

MyClass myclass();
std::map<std::string, std::string> platforms = myclass.getPlatforms();
std::map<std::string, std::string>::iterator definition;
for (definition = platforms.begin();
     definition != platforms.end();
     ++definition){
  std::cout << (*definition).first << std::endl;
}

在 Code2 中,我刚刚创建了一个新的地图变量来保存从 getPlatforms() 函数返回的地图。

无论如何,在我的真实代码中(我无法从中发布真实代码,但它直接对应于这个概念)第一种方式(代码 1)会导致运行时错误,无法访问某个位置的内存。

第二种方法有效!

您能告诉我这两段不同代码之间发生了什么的理论基础吗?

【问题讨论】:

    标签: c++ dictionary iterator


    【解决方案1】:

    getPlatforms() 按值返回地图,而不是按引用返回,这通常是个坏主意。

    你已经举了一个例子来说明为什么这是一个坏主意:

    getPlatforms().begin() 是地图上的迭代器,该迭代器在使用之前就消失了,getPlatforms().end() 是来自同一原始地图的不同副本上的迭代器。

    【讨论】:

    • Joe 指出 getPlatforms() 是 const,因此通过非 const 引用返回地图将是一个问题。 (从 const 方法按值返回没有问题,但正如我试图解释的那样,按值返回是个坏主意)。因此,您想通过const&amp; 返回,在这种情况下,Joe 关于 const_iterator 的说法将变得正确。
    • 谢谢。如果您不介意,还有几个后续问题:(1)如何通过引用返回地图? (2) 什么时候通过引用返回对象比通过值返回更好?
    • 您使用 std::map&lt;std::string, std::string&gt; const&amp; getPlatforms() const; 而不是 std::map&lt;std::string, std::string&gt; getPlatforms() const; 然后您需要为您的代码切换到 const_iterator1。
    • 要使 code2 与更正后的 code1 相媲美,您将进行上述注释中描述的两项更改,并将 std::map&lt;std::string, std::string&gt; platforms = myclass.getPlatforms(); 更改为 std::map<:string std::string> const& 平台 = myclass.getPlatforms();`
    • 通过const&amp;而不是值返回任何持久的大对象几乎总是更好。然后调用者可以决定是通过const&amp;获取还是通过值获取来复制。如果你想将内部成员暴露给外部修改,那么通过&amp;而不是const&amp;返回它
    【解决方案2】:

    您能告诉我这两段不同代码之间发生了什么的理论基础吗?

    当您按值返回时,您返回的是数据的深层副本。

    当您调用myclass.getPlatforms().begin(); 和myclass.getPlatforms().end(); 时,您实际上是在构造数据的两个副本,然后从一个副本获取开始迭代器,从另一个副本获取结束迭代器。然后,比较两个迭代器是否相等; 这是未定义的行为。

    导致运行时错误,无法访问某个位置的内存。

    这是因为definition被初始化了,然后用来创建它的临时对象被删除,使迭代器指向的数据失效。然后,您尝试通过迭代器使用数据。

    【讨论】:

      【解决方案3】:

      您遇到的一个问题是您应该使用const_iterator 而不是iterator。这是因为函数getPlatforms 是const 限定的,而映射中的函数iterator begin() 不是;您必须使用 const 限定 const_iterator begin() const 来明确告诉编译器您不会修改该类的任何成员。

      注意:这只是代码 1 的情况,顺便说一句,它应该返回 const&amp;

      【讨论】:

      • 错了。 getPlatforms() 的返回值是not const-qualified,因此完全可以使用变异迭代器。
      • @AngeW - 对不起,我忘了澄清它只发生在 const&amp; 返回类型
      • const 不是问题。他们有一个不再存在的对象的迭代器。
      • @NathanOliver - 但如果您返回 const&amp;,代码 1 会像代码 2 一样编译和运行
      猜你喜欢
      • 1970-01-01
      • 2018-09-02
      • 1970-01-01
      • 2021-04-15
      • 2011-07-31
      • 2016-01-17
      • 2014-06-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多