返回首页

简介
在C开发,是一个必须有:它已被视为可能的,抽象的,通用性,和可扩展的简单。其中一个重要的通用概念,STL的C开发人员熟悉的是迭代器的概念。
迭代器是用于访问容器中的元素没有露出容器是如何实现(例如,一个向量,列表,红黑树,哈希集合,队列等)。迭代器是通用编程的核心,因为他们是一个容器和应用程序之间的接口。应用程序需要访问容器中的元素,但它们通常并不需要知道如何在容器中的元素。迭代器使得它可以编写泛型算法,对不同类型的容器操作。
例如,下面的代码片段公开容器的性质 - 一个载体。

void process(const std::vector<E>& v)

{

    for (unsigned i = 0; i < v.size(); ++i) {

        process(v[i]);

    }

}

如果我们要对列表中的相同功能的操作,我们写一个单独的函数。或者,如果我们后来决定,列表或散列设置是作为一个容器更合适,我们需要重写代码,我们到处访问的载体。这可能需要很多的变化在许多文件。这个容器进行对比,具体探视以下计划:{C}
使用迭代器的概念,我们有一个容器"C"的通用处理,无论是一个向量,列表,哈希集合,或任何数据结构,在它的API提供的迭代器的。更妙的是,我们可以写一个通用的进程的功能,只需要一个迭代的范围内,不承担责任,容器都有一个开始()和end()方法:
template <typename Iterator>

void process(Iterator begin, Iterator end)

{

     for (; itr != end; ++itr) {

         process(*itr);

     }

}

一个STL迭代器是一种商品,作为一个标量类型的行为:它可以在堆上分配它可以被复制它可以通过值传递它可以被分配到
迭代器的本质是捕获以下API:
template <typename T>

class Itr {

public:

     Itr();

      Itr();

     Itr(const Itr& o);                   // Copy constructor

     Itr& operator=(const Itr& o);        // Assignment operator

     Itr& operator++();                   // Next element

     T&   operator*();                    // Dereference

     bool operator==(const Itr& o) const; // Comparison

     bool operator!=(const Itr& o) const { return !(*this == o); }

}

通常情况下,容器将提供一个begin()和结束()方法,该方法构建的迭代器,表示容器的范围。如果容器是从一个STL容器派生这些方法开始/结束的写作是一件容易的事,如果容器有一个数据成员,是一个STL容器,或迭代器,如果是标量类型,像一个指针或索引。
这是更复杂,如果我们想取消引用同一类型的对象,但必须访问多个容器,可能的不同类型,或以不同的方式访问容器的迭代器迭代器。例如,让我们假设我们有一些属性(例如,颜色)存储在多个货柜,其中有一些不同类型的对象。我们想访问的所有对象,独立的容器和它们的类型的数量,或者我们想访问一个给定的颜色的对象,或者我们想访问的对象,满足一些谓词:
Itr<E> begin(); // This give the range to visit

Itr<E> end();   // all the elements of type E      



Itr<E> begin(const Color& color); // Same as above but only for the

Itr<E> end(const Coir& color);    // elements of the given color      



class Predicate {

public:

    bool operator()(const E& e);

};      



Itr<E> begin(Predicate& p); // Same as above but only for the

Itr<E> end(Predicate& p);   // elements that satisfy the predicate

在这种情况下,迭代器是多像一个指针或索引的标量类型复杂:它需要跟踪,其中容器,它是目前访问,或需要检查它的颜色或谓词。在一般情况下,迭代可能有数据成员,以便它能够履行其任务。同时,我们要因式分解的代码和重用通用的迭代方法,写作时更有针对性的迭代器,例如,访问一个特定的颜色元素,要使用的下一个元素的方法Itrlt; EGT;::operator()的。这是可以做到有Itrlt; EGT;是一个虚拟的类,派生类来实现不同的迭代器。例如:
class E {

public:

     Color& color() const;

};      



template <typename E>

class ColoredItr<E> : public Itr<E> {

private:

     typedef Itr<E> _Super;

public:

     ColoredItr<E>(const Color& color) : Itr<E>(), color_(color) {}

     virtual  ColoredItr<E>;

     virtual ColoredItr<E>& Operator++() {

        for (; _Super::operator*().color() != color_; _Super::operator++());

        return *this;

     }

private:

     Color color_;

};

我们想一个通用的迭代器,以满足上述所有要求:它可以在堆上分配它可以被复制它可以通过值传递它可以被分配到它取消引用同一类型它可以访问多个容器它可以访问不同类型的容器它可以在任意礼仪访问的容器
这可以实现如下:
template<typename E>

class ItrBase {

public:

     ItrBase() {}

     virtual  ItrBase() {}

     virtual void  operator++() {}

     virtual E&    operator*() const { return E(); }

     virtual ItrBase* clone() const { return new ItrBase(*this); }

     // The == operator is non-virtual. It checks that the

     // derived objects have compatible types, then calls the

     // virtual comparison function equal.

     bool operator==(const ItrBase& o) const {

         return typeid(*this) == typeid(o) && equal(o);

     }

protected:

     virtual bool equal(const ItrBase& o) const { return true; }

};      



template<typename E>

class Itr {

public:

     Itr() : itr_(0) {}

      Itr() { delete itr_; }

     Itr(const Itr& o) : itr_(o.itr_->clone()) {}

     Itr& operator=(const Itr& o) {

         delete itr_; itr_ = o.itr_->clone(); return *this;

     }

     Itr&  operator++() { ++(*itr_); return *this; }

     E&    operator*() const { return *(*itr_); }

     bool  operator==(const Itr& o) const {

         return (itr_ == o.itr_) || (*itr_ == *o.itr_);

     }

     bool  operator!=(const Itr& o) const { return !(*this == o); }      



protected:

     ItrBase<E>* itr_;

};

ItrBase类的类层次结构的顶层。 ITR是简单的包装上一个ItrBase指针,所以它可以在堆上分配 - 实际执行ItrBase派生类可以有一个任意大小的。注意ITR复制和赋值操作符如何通过ItrBase实施:clone()方法,使ITR作为一个标量类型的行为。最后但并非最不重要,(非虚拟)ItrBase::操作符==等于运算符类型,然后调用(虚拟)平等方法对虚拟子类的平等平等首先检查。 ItrBase不是一个纯虚的原因是,它可以很方便地用来表示一个空的范围,即,范围(ItrBase(),ItrBase())是空的。
E型元素的容器迭代只需要从ItrBaselt; EGT,及工厂提供的begin()和end()方法;任何专门的迭代器返回对象类型Itrlt EGT;
例如,让我们假设我们有一个ES的容器C,以及我们想要一个iterator访问:所有的元素C,可能与重复;所有的元素,没有重复的C。
这是可以做到如下:

回答

评论会员:NEtiger 时间:2012/01/26
我是因为喜欢
评论会员:Stefan63 时间:2012/01/26
我很欣赏你非常整洁这一主题的代表性,但我很认真地想关于你的选择分配迭代器(如果它是一种选择)通过投票new和delete。从语义上说,这是一个有效的和优雅的选择,但实际上,调用new和delete每一个iterator(不是引用的数据,请不要忘记!)正在复制的时间似乎太低效。

思想的情况下,我是这样的:我们的应用程序往往会造成较长数据列表,数千甚至数百数千元素。在堆上。目前,我们正在使用这些要素分配的malloc和free,因为new和delete会减慢抓取申请。我们有这些名单上的工作算法吨,和许多创建这些元素指针的临时副本,每个元素被走过。如果这些都是在堆中分配的,我们的应用程序,将有效地阻止!

您可能认为这是一个特例,但恕我直言,迭代器 - 以及专门设计的迭代器 - 应预计将投入到工作在大型数据集。和实施不应该承担一定的方法,如转让或复制将很少使用

因此,我不认为它是一个好主意,如果没有需要使用堆在你所有的迭代器实现。栈应该足以转让和复制。当然,这需要一些重新设计,我真的没有想到到底。

因此,有一个令人信服的理由使用新的和内部的迭代基地实施删除
评论会员:?ocoudert 时间:2012/01/26
让我们来看看我们自己设置的约束:
- 你想拥有一个迭代器,你可以在堆上分配。所以,你需要有一个指针的包装(因为编译器不知道迭代器的数据成员的大小)
- 你想通过迭代器拷贝。所以,你需要一个克隆方法。
- 你要分配的迭代器。因此,你需要删除/克隆调用赋值运算符。

如果要求删除/克隆是昂贵的,你会得到一个低效的实施。在你给的例子,迭代器是一个真正的指针列表细胞,所以delete操作符是一个no - op,克隆操作员只需复制指针值。因此,开销是微不足道的。我错过的东西
评论会员:?Stefan63 时间:2012/01/26
也许我是一个失去了一些东西,但我的理解是,任何* *调用删除,将"贵",除非你调用它一个0指针。我不知道任何其他情况下,令你表示删除一个no - op。如果有,我缺少的东西主要

您可以添加
virtual void ItrBase::assign(ItrBase&) {}
然后改变你的分配方法
Itr& Itr::operator=(const Itr& o) {(*itr_).assign(*o.itr_); return *this}

这将消除新的需要,只删除任务的目的。

我不认为这种变化的任何问题。即使出于某种原因,你需要为目的分配一个iterator对象调用构造函数和析构,你可以嵌入在:分配()方法,使用的地方调用析构函数和构造 - 这仍然是最好的实际分配的内存分配
评论会员:。ocoudert 时间:2012/01/26
您是正确的,那肯定是通过一个"分配"的方法,尽可能避免显式调用,删除/克隆。好点
评论会员:!ocoudert 时间:2012/01/26
尝试调用下一个结果(仅在VS2010很好),你会发现已发行的地方:

Itrlt; STD:listlt; intgt; GT; ITR;
Itrlt; STD:listlt; intgt; GT * PITR = ITR;
ITR = * PITR;

所以它的Itrlt; EGT;:运营商=(...) - 没有检查"这个"上周二,7月13日,修改,2010 4:27
评论会员:ocoudert 时间:2012/01/26
你说得对,如果你要分配ITR本身,你想保存调用析构函数和克隆。因此,赋值运算符应该是:


     Itr& operator=(const Itr& o) {

         if (itr_ != o.itr_) { delete itr_; itr_ = o.itr_->clone(); } 

         return *this;

     }


评论会员:ocoudert 时间:2012/01/26
感谢校正
。文章的一致性成为很多附近的模板般的状态,所以它的时间对我来说,以重新的投票{S0}
评论会员:吉姆Crafton 时间:2012/01/26
顺便说一句可能是向正确的代码,使其编译结
?我在谈论一些签名的改变如

ItrAll类:公共ItrBaselt; EGT; 模板LT; TYPENAME E,TYPENAME Con​​tainergt类ItrAll:公共ItrBaselt; EGT;

ItrNoRepeat类:公共ItrAll LT模板; TYPENAME E,TYPENAME Con​​tainergt类ItrNoRepeat:公共ItrAlllt,E,Containergt,

Itrlt; EGT;开始(Containeramp; C,BOOL noRepeat = FALSE)模板LT; TYPENAME E,TYPENAME Con​​tainergt; Itrlt; EGT;开始(Containeramp; C,BOOL noRepeat = FALSE)

Itrlt; EGT;结束(Containeramp; C,BOOL noRepeat = FALSE)模板LT; TYPENAME E,TYPENAME Con​​tainergt; Itrlt; EGT;结束(Containeramp; C,BOOL noRepeat = FALSE)

和定义

的typedef ItrAll _Super; 的typedef ItrAlllt,E,Containergt; _Super;

集装箱:迭代itr_; typename的集装箱:迭代itr_;
2010年修订于7月13日,上午5:10
评论会员:ocoudert 时间:2012/01/26
大多数代码应与您最喜爱的g编译器编译。例如,你的第一个例子:

class ItrAll : public ItrBase<E> {};

你真的不需要有一个"模板"的宣言,因为你实例化模板ItrBase定义ItrAll
评论会员:。ocoudert 时间:2012/01/26
什么是您正在使用的GCC版本或可能实施某处E级以及一个名为模板的类
克我的构建系统(REHL 5.0)上安装了从VS2010的设置同样的错误,如VC。

GCC版本:
G - V
使用内置的规格。
目标:x86_64的,RedHat的Linux的
配置:.. /配置 - 前缀= / USR - 迪尔=的/ usr / share / man中 - infodir =的/ usr /共享/信息 - 启用共享 - 启用线程= POSIX - 使检查=释放 - 系统的zlib - 启用__cxa_atexit - 禁用libunwind的例外 - 启用,与libgcj -多文件 - 使语言= C,C,objc,OBJ - C,JAVA,FORTRAN,ADA - ENABLE - JAVA - AWT = GTK - 禁用DSSI - 启用插件 - 与Java的家庭= / usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre - - CPU =通用 - 主机= x86_64上的RedHat Linux的
线程模型:POSIX
gcc版本4.1.1 20070105(红帽4.1.1-52)

无论如何,如果它有可能在一些版本的GCC编译再冷静。修改日(星期二),7月13日,2010 7:01