如何在C++中应用DRY原则到迭代器?(迭代器、const迭代器、反向迭代器、const反向迭代器)

5

好的,所以我现在有两个(完全没有关联的不同项目)类正在使用迭代器。其中一个具有iteratorreverse_iterator按预期工作,而另一个当前具有iterator和半破损的const_iterator(具体而言,因为const_iterator派生自iterator,所以代码LinkedList<int>::iterator i = const_list.begin()是有效的,并允许您修改常量定义的列表...)。
我打算将这四种类型都添加到这个类中...如果我能的话。

如何最小化复制/粘贴代码并仅更改返回类型?创建一个名为base_iterator的基类进行继承?创建一个iteratorconst_iterator并从中继承?从某些std ::类继承?如果这些情况中的任何一种是“最佳”方法,那么哪些代码放在哪里?
也许没有任何替代方案是好的?我很迷茫,在找不到太多参考资料。

任何建议都将不胜感激,但请记住我对该主题(迭代器和C ++总体上尤其是OOP)还不熟悉。我曾经试图研究GCC附带的头文件,但徒劳无功,它们不完全是我要找的教程。


标准库实现通常不是学习任何语言的好选择。它们经常需要处理外部接口(例如操作系统、其他语言),可能需要向后兼容上一个十年的代码,以及其他对你并不适用的因素。总之,它们不是为教学而编写的。好书是必须的,并且会更好地帮助你学习。 - Roger Pate
6个回答

3

这其实非常简单。

首先,看一下Boost.Iterator库。

第二步:你需要声明一个基类(在示例中有详细说明如何进行),它将类似于这个。

template <class Value>
class BaseIterator: boost::iterator_adaptor< ... > {};

您可以实现操作以在其中移动指针。请注意,由于它是已经存在的迭代器的适应,因此只需几个步骤即可实现。这真的很令人印象深刻。

第三步,您只需使用const和非const版本进行typedef:

typedef BaseIterator<Value> iterator;
typedef BaseIterator<const Value> const_iterator;

该库明确向您展示如何使const_iterator版本可以从iterator版本构建。
第四,对于反向迭代器,有一个特殊的reverse_iterator对象,它建立在一个常规迭代器上并向后移动 :)
总之,这是一种非常优雅而完全功能的方式,用于在自定义类上定义迭代器。
我经常编写自己的容器适配器,这不仅仅是为了DRY,而是为了节省打字时间!

+1 for boost::iterator_adaptor。我也使用它,对我来说效果非常好。 - n1ckp

3
有时,盲目应用所谓的DRY规则(不要重复自己,对于那些不熟悉的人来说)并不是最佳方法。特别是当你刚接触这门语言(C++和迭代器)以及OOP本身(方法论)时,试图尽量减少你需要编写的代码数量在现阶段没有太大好处。
我会使用适当的代码实现这两个迭代器。也许在你更有经验掌握了这门语言、工具和技术后,再回过头来看是否可以通过提取公共代码来减少代码量。

你是否已经不断地重复DRY的定义,以至于现在已经下意识地做到了呢? :P - Roger Pate
这是一个很好的建议,我采纳了。我创建了两个独立的基类,iterator和const_iterator(都是std :: iterator的子类),并以iterator为基础创建了reverse_iterator,以reverse_iterator为基础创建了const_reverse_iterator。迭代器的总行数约为70-75。对于4种不同的类型来说还不错。这不包括即将到来的内联文档,但包括足够的空格使其易读。 :) - exscape
虽然“先实现,后重构”似乎是一个不错的通用建议,但这个答案似乎是在实现迭代器时的一种推卸责任的做法,因为应该知道哪些代码经常会被复制。虽然iteratorreverse_iterator在实现上可能有很大的差异,但iteratorconst_iterator 应该基本相同(至少对于任何合理的实现)。 - jamesdlin

1

将迭代器从const_iterator派生,而不是相反的方向。适当地使用const_cast(作为实现细节,对用户未公开)。这在简单情况和模型中非常有效,其中“迭代器是const_iterators”直接成立。

当在您的代码中需要澄清注释时,编写单独的类。您可以使用本地化宏为您生成类似的代码,以避免重复逻辑:

struct Container {
#define G(This) \
This& operator++() { ++_internal_member; return *this; } \
This operator++(int) { This copy (*this); ++*this; return copy; }

  struct iterator {
    G(iterator)
  };
  struct const_iterator {
    G(const_iterator)
    const_iterator(iterator); // and other const_iterator specific code
  };
#undef G
};

宏的作用域/本地化很重要,当然,只有在实际帮助您的情况下才使用它——如果对于您来说会导致代码不易读,请明确地输入它。

关于反向迭代器:在许多情况下,您可以使用std::reverse_iterator将“正常”的迭代器包装起来,而不是重写它们。

struct Container {
  struct iterator {/*...*/};
  struct const_iterator {/*...*/};

  typedef std::reverse_iterator<iterator> reverse_iterator;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
};

0

Maxim1000所建议的的更具体版本:

#include <type_traits>

template<typename Container, bool forward>
class iterator_base
{
public:
    using value_type =
        typename std::conditional<std::is_const<Container>::value,
                                 const typename Container::value_type,
                                 typename Container::value_type>::type;

    iterator_base() { }

    // For conversions from iterator to const_iterator.
    template<typename U>
    iterator_base(const iterator_base<U, forward>& other)
    : c(other.c)
    {
        // ....
    }

    value_type& operator*() const
    {
        // ...
    }

    iterator_base& operator++()
    {
        if (forward)
        {
            // ...
        }
        else
        {
            // ...
        }
    }

    iterator_base& operator++(int)
    {
        iterator_base copy(*this);
        ++*this;
        return copy;
    }

private:
    Container* c = nullptr;
    // ...
};

using iterator = iterator_base<self_type, true>;
using const_iterator = iterator_base<const self_type, true>;

using reverse_iterator = iterator_base<self_type, false>;
using const_reverse_iterator = iterator_base<const self_type, false>;

0

LinkedList<int>::iterator i = const_list.begin()你的begin方法是什么样子的?通过学习STL,你可以看到容器定义了两个这样的方法,具有以下签名:

const_iterator begin() const;
iterator begin();

从一个被标记为const的对象中获取一个iterator不应该成为问题。我认为这里不适用DRY原则。


他目前(不正确地)让const_iterator从iterator派生,因此使用通常的const /非const重载的begin函数时,他的代码仍然能够将任何const_iterator隐式转换为iterator。 - Roger Pate

0

曾经我使用了以下方法:

  1. 创建一个模板类 common_iterator
  2. 为“iterator”和“const_iterator”添加 typedefs
  3. 在“common_iterator”中添加一个以“iterator”类型为参数的构造函数

对于“iterator”,额外的构造函数将替换默认的复制构造函数,在我的情况下,它等效于默认的复制构造函数。

对于“const_iterator”,这将是一个额外的构造函数,可以从“iterator”构造“const_iterator”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接