【问题标题】:How do I apply the DRY principle to iterators in C++? (iterator, const_iterator, reverse_iterator, const_reverse_iterator)如何将 DRY 原则应用于 C++ 中的迭代器? (迭代器,const_iterator,reverse_iterator,const_reverse_iterator)
【发布时间】:2010-12-19 02:21:17
【问题描述】:

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

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

感谢任何建议,但请记住,我是该主题的新手(一般来说,迭代器和 C++,尤其是 OOP)。我尝试研究 GCC 附带的头文件,但徒劳无功——它们并不是我正在寻找的教程。

【问题讨论】:

  • Stdlib 实现对于任何语言来说通常都是不好的学习选择。他们经常需要处理外部接口(例如操作系统、其他语言),可能需要向后兼容过去十年的代码,以及其他根本不适用于您的因素。总而言之:它们不是以教学为目标而编写的。一本好书是必须的,它会更好地为您服务。

标签: c++ inheritance iterator dry code-reuse


【解决方案1】:

有时,一揽子应用所谓的 DRY 规则(Don't Repeat Yourself,对于那些不熟悉的人)并不是最好的方法。尤其是如果您不熟悉该语言(C++ 和迭代器)和 OOP 本身(方法),那么尝试尽量减少现在需要编写的代码量几乎没有什么好处。

我会为每个迭代器使用适当的代码来实现这两个迭代器。也许在您对语言、工具和技术有了更多经验之后,然后回去看看是否可以通过分解通用代码来减少代码量。

【讨论】:

  • 您是否不得不重复 DRY 的定义,以至于您现在反射性地这样做? :P
  • 可能是合理的建议,我接受了。我创建了两个独立的基类,iterator 和 const_iterator(都是 std::iterator 的子类),并且基于 iterator 的 reverse_iterator,以及基于 reverse_iterator 的 const_reverse_iterator。迭代器的总行数约为 70-75。对于 4 种不同的类型来说还不错。这不包括即将发布的内联文档,但包含足够的空格以使其可读。 :)
  • 虽然“先实现,后重构”似乎是很好的一般建议,但这个答案似乎是实现迭代器的一种逃避,应该知道什么代码经常被重复。虽然iteratorreverse_iterator 在实现上可能存在显着差异,但iteratorconst_iterator应该大部分相同(至少对于任何合理的实现而言)。
【解决方案2】:

其实非常简单。

首先,看看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 表示 boost::iterator_adaptor。我也使用它,它对我很有用。
【解决方案3】:

使迭代器从 const_iterator 派生,而不是相反。适当地使用const_cast(作为实现细节,不向用户公开)。这在“迭代器是 const_iterators”的简单案例和模型中非常有效。

当这个开始需要在你的代码中澄清cmets,然后编写单独的类。您可以使用本地化宏为您生成类似的代码,避免重复逻辑:

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;
};

【讨论】:

    【解决方案4】:

    LinkedList&lt;int&gt;::iterator i = const_list.begin() 你的 begin 方法是什么样的?通过研究 STL,您可以看到容器定义了两个具有以下签名的此类方法:

    const_iterator begin() const;
    iterator begin();
    

    从一个限定为 const 的对象获取iterator 应该没有问题。我不认为 DRY 在这里适用。

    【讨论】:

    • 他目前(错误地)将 const_iterator 派生自迭代器,因此对于 begin 的常用 const/非 const 重载,他的代码仍然能够将任何 const_iterator 隐式转换为迭代器。
    【解决方案5】:

    一旦我使用了以下方法:

    1. 制作模板类 common_iterator
    2. 为“iterator”和“const_iterator”添加类型定义
    3. 向“common_iterator”添加一个采用“迭代器”类型的构造函数

    对于“迭代器”,附加构造函数将替换默认的复制构造函数,在我的例子中它相当于默认的复制构造函数。

    对于“const_iterator”,它将是一个额外的构造函数,允许从“iterator”构造“const_iterator”

    【讨论】:

      【解决方案6】:

      what maxim1000 suggested 的更具体版本:

      #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>;
      

      【讨论】:

        猜你喜欢
        • 2011-12-07
        • 2020-12-22
        • 1970-01-01
        • 2012-09-14
        • 2021-02-11
        • 1970-01-01
        • 2011-07-03
        • 1970-01-01
        • 2017-09-07
        相关资源
        最近更新 更多