【问题标题】:Create a well-behaved iterator for a write-to-pointer API为写入指针 API 创建一个行为良好的迭代器
【发布时间】:2015-03-14 05:21:50
【问题描述】:

我必须为仅具有旧式“写出指针”访问器的 API 创建一个迭代器。有问题的 API 是 OGR 的;有问题的课程之一是OGRLineString(供参考:http://www.gdal.org/classOGRLineString.html)。该类存储了一些点,可以使用以下getter方法访问:

 void OGRLineString::getPoint(int pos, OGRPoint *out)

为了使用访问器,创建一个新的OGRPoint 对象并将指向它的指针传递给方法,该方法将数据写入分配的对象。例如:

OGRPoint *p = new OGRPoint();
lineString->getPoint(0, p);

现在,我想实现一个(类似 STL)的迭代器。即使我在各处放置大警告标志,说明提供的OGRPoints 是不可修改的(即const),并且如果另一段代码修改了正在迭代的OGRLineString,我也不会更新,我得到OGRPoint const &operator*() const 的内存泄漏问题,因为 API 要求我传递自定义分配的 OGRPoint 实例,但迭代器必须分配一个。另外,当迭代器本身被删除时,迭代器返回的OGRPoints 不应该被删除。此外,OGRLineString 不存储为getPoint 复制的OGRPoint 的实际实例,而是存储x/y/z 坐标的简单结构;所有必需的附加信息(例如,空间参考)都复制到访问器中。因此,一个简单的#define private public hack 将无济于事。

是否有任何理智/干净的方式来添加迭代器而不修改OGRLineString 的原始来源?例如,有没有办法向原始类添加特性或修改它,就像 Ruby 的“猴子补丁”特性那样?或者观察容器的生命周期以清理迭代器返回的OGRPoint 实例?

【问题讨论】:

  • 你应该看看vector<bool>::operator[]是如何工作的,它会告诉你如何解决这个怪癖。
  • 你现在在做什么导致内存泄漏?为什么迭代器不管理OGRPoint?你的迭代器是做什么的?
  • OGRPoint 可以与价值语义一起使用吗?即它有复制构造函数等?在这种情况下,请这样做,而不是“新建”。如果没有,请使用智能指针

标签: c++ pointers stl iterator ogr


【解决方案1】:

这假设 OGRPoint 是可复制构造的。如果没有,请使用智能指针。

#include <iterator>

#include <ogr_geometry.h>

struct OGR_SimpleCurve_Points_Iterator : std::iterator< std::random_access_iterator_tag, const OGRPoint >
{
    OGR_SimpleCurve_Points_Iterator( OGRSimpleCurve* curve=nullptr, int index=0 )
        : curve(curve), index(index) {}

    OGR_SimpleCurve_Points_Iterator& operator++() { ++index; return *this; }
    OGR_SimpleCurve_Points_Iterator operator++(int) { OGR_SimpleCurve_Points_Iterator ret(*this); ++index; return ret; }
    OGR_SimpleCurve_Points_Iterator& operator--() { --index; return *this; }
    OGR_SimpleCurve_Points_Iterator operator--(int) { OGR_SimpleCurve_Points_Iterator ret(*this); --index; return ret; }

    OGR_SimpleCurve_Points_Iterator& operator+=(int n) { index+=n; return *this; }
    OGR_SimpleCurve_Points_Iterator& operator-=(int n) { index-=n; return *this; }

    OGR_SimpleCurve_Points_Iterator operator+(int n) { return OGR_SimpleCurve_Points_Iterator{curve,index+n}; }
    OGR_SimpleCurve_Points_Iterator operator-(int n) { return OGR_SimpleCurve_Points_Iterator{curve,index-n}; }

    int operator-(const OGR_SimpleCurve_Points_Iterator& other) { return index-other.index; }

    OGRPoint operator*() { OGRPoint p; curve->getPoint(index,&p); return p; }

    OGRPoint operator[](int ofs) { OGRPoint p; curve->getPoint(index+ofs,&p); return p; }

    bool operator == ( const OGR_SimpleCurve_Points_Iterator& other ) { return index==other.index; }
    bool operator != ( const OGR_SimpleCurve_Points_Iterator& other ) { return index!=other.index; }
    bool operator  > ( const OGR_SimpleCurve_Points_Iterator& other ) { return index >other.index; }
    bool operator >= ( const OGR_SimpleCurve_Points_Iterator& other ) { return index>=other.index; }
    bool operator  < ( const OGR_SimpleCurve_Points_Iterator& other ) { return index <other.index; }
    bool operator <= ( const OGR_SimpleCurve_Points_Iterator& other ) { return index<=other.index; }

private:
    OGRSimpleCurve* curve;
    int index;
};

OGR_SimpleCurve_Points_Iterator begin( OGRSimpleCurve* curve )
{
    return OGR_SimpleCurve_Points_Iterator{curve};
}

OGR_SimpleCurve_Points_Iterator end( OGRSimpleCurve* curve )
{
    return OGR_SimpleCurve_Points_Iterator{curve,curve->getNumPoints()};
}

【讨论】:

  • 非常感谢!经过相当多的工作(并阅读了与 C++ 相关的细节),这个版本最适合我的需要。复制构造与共享指针与原始指针的评论终于让我真正理解了我面临的问题。再次感谢!
  • 这看起来像是一个完整的迭代器,但是没有operator-&gt;,这个迭代器只能满足output_iterator的概念。它也缺少所有类型定义。如果添加这些、默认构造函数和operator+(int,iterator),那么它可以收到完整的random_access_iterator_tagstackoverflow.com/a/8054856/845092
【解决方案2】:
class this_is_my_iterator;

class OutputOGRPoint {
    explicit OutputOGRPoint(this_is_my_iterator* parent_)
        :parent(parent_), p(new OGRPoint()) 
    {}
    ~OutputOGRPoint();
    operator OGRPoint *() {return p;}

    OutputOGRPoint(OutputOGRPoint &&)=default;
    OutputOGRPoint&operator=(OutputOGRPoint &&)=default;
private:    
    this_is_my_iterator* parent;
    std::unique_ptr<OGRPoint> p;
};

class this_is_my_iterator {
    OutputOGRPoint operator*()(return OutputOGRPoint(this);}
private:
    friend OutputOGRPoint;
    void apply_operator_star_changes(OGRPoint *p);
};

inline OutputOGRPoint::~OutputOGRPoint()
{parent->apply_operator_star_changes(p);}

当您需要管理返回值生命周期的代码时,使用此“伪指针”返回类型。它还可以用于为实际上不存在的内部成员“返回可变引用”。 vector&lt;bool&gt; 在内部使用位域而不是 bool 对象,但使用相同的模式从 operator[] 返回可变引用。

【讨论】:

  • 输出指针的概念对我来说是新的,非常感谢!感谢您和 sp2danny 的回答,我学到了很多东西。后来我才看到我还依赖于 OGR 的工厂类(用于 WKT/WKB 读/写),因此输出指针似乎更具侵入性。尽管如此,还是感谢您提供的扩展我的 C++ 知识的建议!
猜你喜欢
  • 2021-12-06
  • 1970-01-01
  • 2021-08-11
  • 2010-10-19
  • 2023-04-04
  • 2019-12-15
  • 1970-01-01
  • 1970-01-01
  • 2016-07-24
相关资源
最近更新 更多