如何实现类似std的迭代器的自定义实现?

Ben*_*Ben 8 c++ iterator std

我写了一个非常简单的文件管理数据库,基本上看起来像这样:

class FileDB
{
public:
    FileDB(std::string dir) : rootDir(dir) { }

    void loadFile(std::string filename, File &file) const;
    void saveFile(std::string filename, const File &file) const;

private:
    std::string rootDir;
}
Run Code Online (Sandbox Code Playgroud)

现在我想迭代遍历数据库中包含的所有文件,例如使用std::iterator:

void iterateFiles()
{
    FileDB filedb("C:\\MyFiles");

    for (FileDB::iterator file_it = filedb.begin(); file_it != filedb.end(); ++file_it)
    {
        File f = *file_it;
        // do something with file
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经阅读了类似问题的答案,有些人建议推导std::iterator,有些人要使用std::iterator_traits,但我真的不明白该怎么做.尝试实现自定义迭代器时可能出现什么问题?什么是简单而优雅的方式呢?

编辑: 请不要考虑使用提升,我的问题更具概念性.

编辑2:

FileDB的工作方式如下:

  • ROOTDIR

    • foo1
      • BAR1
        • foo1bar1_1.txt
        • foo1bar1_2.txt
      • BAR2
        • foo1bar2_1.txt
        • foo1bar2_2.txt
    • foo2的

      • barM中

        • fooNBarM_x.txt

所以基本上,我可以通过它的名字找到一个文件.

由于我的容器不在内存中,因此我没有指向其数据的指针.所以我的想法是将文件的路径存储在迭代器中.这样,我可以operator==使用字符串比较来实现,因为路径应该是唯一的.从中返回的迭代器fileDB.end()将是一个空字符串,operator*并将调用fileDB::loadFile()其文件路径.

我最担心的是operator++.有了文件名,我可以找到包含目录并搜索下一个文件,但这实际上是无效的.有关如何做到这一点的任何想法?或者我对整个概念完全错了?

LiK*_*Kao 12

自己编写迭代器几乎不可能.向类中添加迭代器的最简单方法是重用现有的迭代器.您应该知道,例如,指针和C++中的迭代器一样好,所以有很多方法可以提供迭代器而无需实际编写自己的迭代器.

这基本上是C++在很多方面的工作方式.它试图通过给图书馆作者带来很多负担,使最终用户的语言易于使用和简单化.即图书馆作家可以写出所有不合适的东西,所以最终用户不必这样做.迭代器通常是库的一部分.

话虽如此,实际上是丑陋的部分:

为了能够编写自己的迭代器,这里有一些你需要注意的事情.

类型特征:

类型特征是一种向C++中的类型添加附加信息的简单机制,即使对于无法自行更改的类型也是如此.例如,对于迭代器,重要的是要知道它迭代的内容(即包含的类型).获取给定迭代器的这些信息的方法很大程度上取决于迭代器.对于实际上是对象的迭代器,您可以在类中添加typedef并使用它们,但对于作为指针的迭代器,您需要从指针类型推断它.为了实现这一点,信息存储在类型特征中,因此代码可以在一个地方查看此信息.这是std::iterator_traits类型特征.

std::iterator_traits处理从std::iterator模板以及任何类型的指针派生的任何事物,无需任何调整.因此,通常最好将其std::iterator作为基础,以避免编写自己的特征专业化.如果你不能这样做,它仍然可以提供必要的特征,但它会更难.

标记类和迭代器类型:

C++中有几种不同类型的迭代器,它们具有不同的行为,可以/不能做很多不同的事情.请查看http://cplusplus.com/reference/std/iterator/,了解可用的迭代器类型以及它们可以执行的操作.这些图并不意味着以面向对象的方式(即,input_iterator既不是a的子类也不是a的基类forward_iterator),而是作为API类型的派生.也就是说,您可以使用为输入迭代器编写的所有算法以及前向迭代器.页面上的表格将告诉您必须为每个类别提供哪些方法.

由于这些类别实际上并不是彼此的子类(它们不应该是,特别是在从不同类型的集合中提交时),因此使用另一种机制来标识每个迭代器的功能.在std::iterator_traits描述每个迭代器时也包含一个空标记类,它告诉迭代器可以做什么以及它不能做什么.如果您不编写自己的特征,则需要std::iterator在实例化时将此标记类提供给模板.

例:

此示例取自迭代器上的cplusplus.com部分:

class myiterator : public iterator<input_iterator_tag, int>
{
  int* p;
public:
  myiterator(int* x) :p(x) {}
  myiterator(const myiterator& mit) : p(mit.p) {}
  myiterator& operator++() {++p;return *this;}
  myiterator operator++(int) {myiterator tmp(*this); operator++(); return tmp;}
  bool operator==(const myiterator& rhs) {return p==rhs.p;}
  bool operator!=(const myiterator& rhs) {return p!=rhs.p;}
  int& operator*() {return *p;}
};
Run Code Online (Sandbox Code Playgroud)

这个迭代器实际上没有意义,因为它只包装一个指针,它也可以直接使用.但它可以作为解释.迭代源自std::iterator作为input_iterator通过提供合适的标签.此外,模板被告知,这个迭代器正在迭代int.所有其他类型,需要哪些difference_type,reference,poiner等会自动由模板infered.在某些情况下,手动更改其中一些类型可能是有意义的(例如std::shared_ptr,pointer有时必须使用它).此迭代器所需的特征也将自动存在,因为它已经派生自std::iterator并且std::iterator_traits知道在哪里可以找到所有必要的信息.


D_E*_*D_E 6

class FileDB
{
class iterator;

public:
    FileDB(std::string dir) : m_rootDir(dir) { m_files = getFileTreeList(); }

    void loadFile(std::string filename, File &file) const;
    void saveFile(std::string filename, const File &file) const;


    iterator begin()
    {
      return iterator(m_files.begin(), *this);
    }
    iterator end()
    {
      return iterator(m_files.end(), *this);
    }

private:
    std::list<std::string> getFileTreeList() const;

private:    
    std::string m_rootDir;
    std::list<std::string> m_files;
}



class FileDB::iterator
{
public:
  iterator(std::list<std::string>::iterator pos, FileDB& owner) : m_pos(pos), m_owner(owner){}

  bool operator==(const iterator& rhs) {return m_pos == rhs.m_pos;}
  bool operator!=(const iterator& rhs) {return m_pos != rhs.m_pos;}
  //etc

  void operator++() {++m_pos;}

  File operator*()
  {
    return openFile(*m_pos); // for example open some file descriptor
  }

  ~iterator()
  {
    closeFile(*m_pos); // clean up!
  }

private:
  std::list<std::string>::iterator& m_pos;
  FileDB& m_owner;
};
Run Code Online (Sandbox Code Playgroud)