【问题标题】:Subtle error when using temporaries to get iterators to a STL container: how to avoid it?使用临时对象获取 STL 容器的迭代器时的细微错误:如何避免?
【发布时间】:2012-04-06 01:24:47
【问题描述】:

让我们考虑这个类:

class X {
    std::map<uint32_t, uint32_t> _map;
public:
    X() { /* Populate the map */ }

    std::map<uint32_t, uint32_t> getTheMap() { return _map; }
};

还有这个错误的代码:

X x;
// Statement 1
std::map<uint32_t, uint32_t>::const_iterator it = x.getTheMap().begin();
// Statement 2
std::map<uint32_t, uint32_t>::const_iterator et = x.getTheMap().end();

for (; it != et; it++) {
    /* Access the map using the iterator it */
}

错误的部分是,在Statement 1Statement 2 中,我得到了一个临时对象的迭代器,该对象将在每个语句的末尾被销毁。 因此,for() 循环内的行为未定义。

getTheMap() 方法的正确用法应该是这样的:

std::map<uint32_t, uint32_t> map = x.getTheMap();
std::map<uint32_t, uint32_t>::const_iterator it = map.begin();
std::map<uint32_t, uint32_t>::const_iterator et = map.end();

for (/* [...] */)

必须注意,X 类有一些严重的设计问题:

  1. _map 应该更好地封装在类中(用于读写访问),因此可以避免使用getTheMap() 方法
  2. 如果确实需要getTheMap() 方法,它可以返回对_map 的引用

但是,给定 X 类“按原样”(

编辑:X 类可以更改,但getTheMap 方法应该存在并按值返回。但是我也在考虑编译器警告。

【问题讨论】:

  • 如果不能更改 X 类,我们必须处理什么?可以隐藏类 X,迫使用户使用其他东西,比如包装类吗?
  • 文档怎么样。
  • @VaughnCato 你是完全正确的:)。我在问题末尾的EDIT 部分添加了一条注释。
  • 继续按值返回的原因是什么?返回对 const 的引用和引用不会破坏旧代码。
  • 你不能做任何事情,除了重新设计类以不蹩脚。如果用户没有意识到这一点,他们不应该编写 C++。另外,如果您通过引用返回地图,您不妨将_map公开。

标签: c++ stl iterator temporary-objects


【解决方案1】:

一种可能性是使用这样的包装器:

class X {
  typedef std::map<uint32_t,uint32_t> Map;
  Map _map;

  struct MapWrap {
    const Map &mapref;

    MapWrap(const Map &mapref_arg)
    : mapref(mapref_arg)
    {
    }

    operator Map() const { return mapref; }
  };


public:
  MapWrap getTheMap()
  {
    return MapWrap(_map);
  }
};

让你得到这个:

X x;
std::map<uint32_t,uint32_t>::const_iterator iter = x.getTheMap().begin(); // error
std::map<uint32_t,uint32_t> m = x.getTheMap(); // no error

这可以防止意外使用临时地图,但会使其在用户必须使用地图副本的地方。

【讨论】:

  • 也称为返回代理对象。
【解决方案2】:

不在 C++03 中。在 C++11 中,标准库应该已经启用了这种保护。

【讨论】:

  • 至少这证实了我的问题有道理:)
【解决方案3】:

您可以尝试使用 std::move 强制 getTheMap() 返回原始对象,但我不确定这是否适用。

如果不是,我想返回成员的 unique/shared_ptr 将是最好的选择。

【讨论】:

    猜你喜欢
    • 2011-05-02
    • 2010-11-26
    • 1970-01-01
    • 1970-01-01
    • 2018-11-29
    • 1970-01-01
    • 2012-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多