抽象类的快速灵活的迭代器

发布于 2024-09-27 19:18:59 字数 2284 浏览 1 评论 0原文

为了以快速灵活的方式遍历数据网格,我设置了一个抽象的、模板化的 GridDataStructure 类。数据应该由 STL 迭代器访问。当有人使用该类时,他不应该担心哪种 STL 迭代器适合特定的子类。

这个问题的解决方案似乎是 使用迭代器隐藏内部容器并实现对基础容器的通用操作。但是,我不明白为什么 begin() 和 end() 成员不再是虚拟的。除此之外,我无法弄清楚 STL 迭代器类的必要方法(如operator++、operator* 等)到底应该在哪里实现。

请您看看我是否有设计错误?对我来说重要的是灵活的设计,但不以牺牲性能为代价。

我的班级设计:

template<class T>
class GridDataStructure
{
public:
    virtual iterator begin() = 0;
    virtual iterator end() = 0;
};

template<class T>
class GridDataUniform : GridDataStructure
{
public:
    GridDataUniform(int size);        

    iterator begin();
    iterator end();

    class iterator : public std::iterator<std::forward_iterator_tag, T> {
    public:
      iterator(Node* p) : node_(p) {}
      ~iterator() {}

      iterator& operator=(const iterator& other);
      bool operator==(const iterator& other);
      bool operator!=(const iterator& other);
      iterator& operator++();
      iterator& operator++(int);
      T& operator*();
      T* operator->();

    private:
      Node* node_;
    };

    private:
        T* griddata;
};

我想以 STL 风格访问我的网格容器,例如:

GridDataStructure<int>::iterator = someGrid->begin(); // where someGrid is an instance of GridDataUniform
std::cout << *(iter) << std::endl;

非常感谢任何帮助。

编辑(10.10.19):添加了嵌套迭代器类

编辑(10.10.20):添加了代码:

template<class T>
class GridDataStructureBase
{
protected:
class BaseIteratorImpl
{
    virtual iterator begin() = 0;
    virtual iterator end() = 0;
    virtual iterator& operator++() = 0;
}

public:
class iterator : std::iterator<std::forward_iterator_tag, T>
{
public:
    iterator(const BaseIteratorImpl& itImpl) {}
    iterator begin() { return itImpl->begin(); }
    iterator end() { return itImpl->end(); }
    iterator& operator++() { return itImpl->operator++() }

private:
    BaseIteratorImpl* itImpl;

};

iterator begin()
{
    iterator* i = new iterator(??);
    return i->begin();
}

iterator end()
{
    return iterator(NULL);
}

};

In order to traverse grids with data in a fast and flexible way I set up an abstract, templated GridDataStructure class. The data should be accessed by STL iterators. When someone uses the class, he should not worry about which kind of STL iterator is appropriate for a specific subclass.

A solution to this problem seems to be Using Iterators to hide internal container and achieve generic operation over a base container. However, I do not get why the begin() and end() members are not virtual anymore. Next to that I could not figure out where exactly the necessary methods for the STL iterator class (like operator++, operator* etc.) should be implemented.

Could you please have a look whether or not I make a design mistake? Important to me is a flexible design, but not at the cost of performance.

My class design:

template<class T>
class GridDataStructure
{
public:
    virtual iterator begin() = 0;
    virtual iterator end() = 0;
};

template<class T>
class GridDataUniform : GridDataStructure
{
public:
    GridDataUniform(int size);        

    iterator begin();
    iterator end();

    class iterator : public std::iterator<std::forward_iterator_tag, T> {
    public:
      iterator(Node* p) : node_(p) {}
      ~iterator() {}

      iterator& operator=(const iterator& other);
      bool operator==(const iterator& other);
      bool operator!=(const iterator& other);
      iterator& operator++();
      iterator& operator++(int);
      T& operator*();
      T* operator->();

    private:
      Node* node_;
    };

    private:
        T* griddata;
};

I would like to access my grid container in STL style, like:

GridDataStructure<int>::iterator = someGrid->begin(); // where someGrid is an instance of GridDataUniform
std::cout << *(iter) << std::endl;

Any help is highly appreciated.

Edit (19.10.10): Added nested iterator class

Edit (20.10.10): Added code:

template<class T>
class GridDataStructureBase
{
protected:
class BaseIteratorImpl
{
    virtual iterator begin() = 0;
    virtual iterator end() = 0;
    virtual iterator& operator++() = 0;
}

public:
class iterator : std::iterator<std::forward_iterator_tag, T>
{
public:
    iterator(const BaseIteratorImpl& itImpl) {}
    iterator begin() { return itImpl->begin(); }
    iterator end() { return itImpl->end(); }
    iterator& operator++() { return itImpl->operator++() }

private:
    BaseIteratorImpl* itImpl;

};

iterator begin()
{
    iterator* i = new iterator(??);
    return i->begin();
}

iterator end()
{
    return iterator(NULL);
}

};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

裸钻 2024-10-04 19:18:59

在该解决方案中,begin 和 end 不需要是虚拟的,因为它们只是调用虚拟的 BaseIteratorImpl::beginBaseIteratorImpl::end

在您的具体情况下,您可以将 beginend 设为虚拟,而不进行任何转发,它将能够执行您想要的操作。您指出的解决方案是,如果您想要在同一结构上使用不同样式的迭代器,而不仅仅是您想要的结构-迭代器配对。

编辑:这里有一些东西可以开始(未经测试或什至编译) - 可能不会编译并且会泄漏(编写析构函数,复制因子,op=,你需要的地方) - 只是为了让你开始这个想法。

template <class T>
class GridIteratorImplBase {
   public:
      virtual GridIteratorImplBase<T>& operator++() = 0;
      virtual T& operator*() = 0;
};

template <class T>
class GridIterator {
   private:
      GridIteratorImplBase<T> *baseImpl;
   public:
      GridIterator(GridIteratorImplBase<T> *b) :baseImpl(b) {}
      GridIterator& operator++() { baseImpl->operator++(); return *this;}
      T& operator*() { return baseImpl->operator*(); }


  // you need to write a dtor, copy ctor and op= or this will leak
  // copy ctor and op= need to make new objects that are copies of baseImpl, dtor needs to delete -- make sure not to share baseImpl between two GridIterator objects
};


template <class T>
class Grid {
   virtual GridIterator<T> begin() = 0;
   virtual GridIterator<T> end() = 0;
};


template <class T>
class GridUniform {

  template <class T>
  class GridUniformIterator : GridIteratorImplBase<T>
      private T* current;
   public:
      GridUniformIterator(T* c) : current(c) {}
      virtual GridIteratorImplBase<T>& operator++() { current++; return *this; }
      virtual T& operator*() { return *current; }
  };

  GridIterator<T> begin() { 
      GridIterator<T> iter(new GridUniformIterator(gridData)); 
      return iter; 
  }
  GridIterator<T> end() { 
      GridIterator<T> iter(new GridUniformIterator(gridData+size));
      return iter; 
  }


  private:
    T* gridData;
    int size;
};

我直接将其输入到此答案的文本区域中 - 而不是编译器。它旨在为您提供想法,以便您可以开始。

  1. begin 和 end 应该创建迭代器,
  2. 迭代器需要能够被复制构造并对其调用operator=。如果你尝试为它们建立一个基类,它们将被强制转换为基类,因此你不能为它们使用 virtual
  3. 为了解决第 2 个问题,你可以让迭代器只保留一个指向迭代器实现的基类的指针。

In the solution, begin and end don't need to be virtual, because they just call BaseIteratorImpl::begin and BaseIteratorImpl::end which are virtual.

In your specific case, you could just make begin and end virtual and not do any forwarding and it would be able to do what you want. The solution you pointed to is if you want different style iterators over the same structure, not just structure-iterator pairings which it seems you want.

EDIT: Here's something to start with (not tested or even compiled) -- might not compile and will leak (write destructors, copy ctors, op=, where you need to) -- just to get you started on the idea.

template <class T>
class GridIteratorImplBase {
   public:
      virtual GridIteratorImplBase<T>& operator++() = 0;
      virtual T& operator*() = 0;
};

template <class T>
class GridIterator {
   private:
      GridIteratorImplBase<T> *baseImpl;
   public:
      GridIterator(GridIteratorImplBase<T> *b) :baseImpl(b) {}
      GridIterator& operator++() { baseImpl->operator++(); return *this;}
      T& operator*() { return baseImpl->operator*(); }


  // you need to write a dtor, copy ctor and op= or this will leak
  // copy ctor and op= need to make new objects that are copies of baseImpl, dtor needs to delete -- make sure not to share baseImpl between two GridIterator objects
};


template <class T>
class Grid {
   virtual GridIterator<T> begin() = 0;
   virtual GridIterator<T> end() = 0;
};


template <class T>
class GridUniform {

  template <class T>
  class GridUniformIterator : GridIteratorImplBase<T>
      private T* current;
   public:
      GridUniformIterator(T* c) : current(c) {}
      virtual GridIteratorImplBase<T>& operator++() { current++; return *this; }
      virtual T& operator*() { return *current; }
  };

  GridIterator<T> begin() { 
      GridIterator<T> iter(new GridUniformIterator(gridData)); 
      return iter; 
  }
  GridIterator<T> end() { 
      GridIterator<T> iter(new GridUniformIterator(gridData+size));
      return iter; 
  }


  private:
    T* gridData;
    int size;
};

I typed this directly in to the text area of this answer -- not a compiler. It's meant to give you the idea so you can get started.

  1. begin and end are supposed to create iterators
  2. iterators need to be able to be copy constructed and have operator= called on them. If you try to have one base class for them, they will get casted to the base, so you can't use virtual for them
  3. To get around #2, you make iterators just hold onto a pointer to a base class of an iterator implementation.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文