Criando meus próprios Iteradores

141

Estou tentando aprender C ++, então me perdoe se esta pergunta demonstrar falta de conhecimento básico, veja bem, o fato é que eu tenho falta de conhecimento básico.

Quero ajuda para descobrir como criar um iterador para uma classe que criei.

Eu tenho uma classe 'Shape' que possui um contêiner de Points. Eu tenho uma classe 'Piece' que faz referência a uma Shape e define uma posição para a Shape. A peça não tem uma forma, apenas faz referência a uma forma.

Quero que pareça que Piece é um contêiner de pontos que são iguais aos da Shape que ele faz referência, mas com o deslocamento da posição da Piece adicionado.

Quero ser capaz de percorrer os Pontos da Peça, como se a Peça fosse um contêiner. Eu li um pouco e não encontrei nada que me ajudou. Ficaria muito grato por qualquer indicação.

Howard May
fonte
6
A publicação de código de amostra ajudaria a descrever o que você está fazendo melhor do que apenas texto em inglês.
Greg Rogers
3
A criação de iteradores personalizados provavelmente não é uma parte superior básica, pelo menos intermediária.
Ldog

Respostas:

41

Você deve usar o Boost.Iterators. Ele contém vários modelos e conceitos para implementar novos iteradores e adaptadores para iteradores existentes. Eu escrevi um artigo sobre esse mesmo tópico ; está na revista ACCU de dezembro de 2008. Ele discute uma solução elegante (IMO) para exatamente o seu problema: expor coleções de membros de um objeto, usando Boost.Iterators.

Se você deseja usar apenas o stl, o livro Josuttis possui um capítulo sobre a implementação de seus próprios iteradores STL.

Roel
fonte
3
Apenas uma observação menor: O livro fala sobre a biblioteca padrão C ++, e não a STL - estes são diferentes, mas se confundem muito (estou / era culpado, também)
CppChris
62

/ EDIT: Entendo, um iterador próprio é realmente necessário aqui (eu li a pergunta primeiro). Ainda assim, estou deixando o código abaixo, pois pode ser útil em circunstâncias semelhantes.


Um iterador próprio é realmente necessário aqui? Talvez seja suficiente encaminhar todas as definições necessárias para o contêiner que contém os pontos reais:

// Your class `Piece`
class Piece {
private:
    Shape m_shape;

public:

    typedef std::vector<Point>::iterator iterator;
    typedef std::vector<Point>::const_iterator const_iterator;

    iterator begin() { return m_shape.container.begin(); }

    const_iterator begin() const { return m_shape.container.begin(); }

    iterator end() { return m_shape.container.end(); }

    const_iterator end() const { return m_shape.const_container.end(); }
}

Isso pressupõe que você esteja usando um vectorinternamente, mas o tipo pode ser facilmente adaptado.

Konrad Rudolph
fonte
talvez ele quer usar o algoritmo de STL ou características funcionais contra a sua classe ...
gbjbaanb
2
A pergunta original realmente diz que o iterador do contêiner de peças deve modificar os valores ao retorná-los. Isso exigiria um iterador separado, embora provavelmente deva ser herdado ou obtido principalmente do original.
workmad3 29/09/08
@gbjbaanb: O bom do meu código é que ele pode ser usado pelos algoritmos STL.
21978 Konrad Rudolph
1
Alguns anos mais tarde e isso ainda é entre os primeiros resultados no Google ... Agora é possível generalizar esta fazendo algo parecido com isto:auto begin() -> decltype(m_shape.container.begin()) { return m_shape.container.begin(); }
user2962533
20

Aqui, projetar um STL como contêiner personalizado é um excelente artigo que explica alguns dos conceitos básicos de como uma classe de contêineres como STL pode ser projetada juntamente com a classe iteradora. O iterador reverso (um pouco mais difícil) é deixado como um exercício :-)

HTH,

Abhay
fonte
15

Você pode ler este artigo do ddj

Basicamente, herde do std :: iterator para realizar a maior parte do trabalho para você.

gbjbaanb
fonte
2
Observe que std::iteratorestá marcado como obsoleto no C ++ 17.
mandrake
2

Escrever iteradores personalizados em C ++ pode ser bastante detalhado e complexo de entender.

Como não consegui encontrar uma maneira mínima de escrever um iterador personalizado, escrevi esse cabeçalho de modelo que pode ajudar. Por exemplo, para tornar a Piececlasse iterável:

#include <iostream>
#include <vector>

#include "iterator_tpl.h"

struct Point {
  int x;
  int y;
  Point() {}
  Point(int x, int y) : x(x), y(y) {}
  Point operator+(Point other) const {
    other.x += x;
    other.y += y;
    return other;
  }
};

struct Shape {
  std::vector<Point> vec;
};

struct Piece {
  Shape& shape;
  Point offset;
  Piece(Shape& shape, int x, int y) : shape(shape), offset(x,y) {}

  struct it_state {
    int pos;
    inline void next(const Piece* ref) { ++pos; }
    inline void begin(const Piece* ref) { pos = 0; }
    inline void end(const Piece* ref) { pos = ref->shape.vec.size(); }
    inline Point get(Piece* ref) { return ref->offset + ref->shape.vec[pos]; }
    inline bool cmp(const it_state& s) const { return pos != s.pos; }
  };
  SETUP_ITERATORS(Piece, Point, it_state);
};

Então você seria capaz de usá-lo como um Contêiner STL normal:

int main() {
  Shape shape;
  shape.vec.emplace_back(1,2);
  shape.vec.emplace_back(2,3);
  shape.vec.emplace_back(3,4);

  Piece piece(shape, 1, 1);

  for (Point p : piece) {
    std::cout << p.x << " " << p.y << std::endl;
    // Output:
    // 2 3
    // 3 4
    // 4 5
  }

  return 0;
}

Também permite adicionar outros tipos de iteradores como const_iteratorou reverse_const_iterator.

Espero que ajude.

VinGarcia
fonte
1

A solução para o seu problema não é a criação de seus próprios iteradores, mas o uso de contêineres e iteradores STL existentes. Armazene os pontos em cada forma em um recipiente como vetor.

class Shape {
    private:
    vector <Point> points;

O que você faz a partir de então depende do seu design. A melhor abordagem é iterar através dos pontos nos métodos dentro do Shape.

for (vector <Point>::iterator i = points.begin(); i != points.end(); ++i)
    /* ... */

Se você precisar acessar pontos fora do Shape (isso pode ser uma marca de um design deficiente), poderá criar nos métodos Shape que retornarão as funções de acesso do iterador para os pontos (nesse caso, também crie um typedef público para o contêiner de pontos). Veja a resposta de Konrad Rudolph para obter detalhes dessa abordagem.

Diomidis Spinellis
fonte
3
Ele ainda precisará criar seu próprio iterador que encaminha solicitações de Piece para as Formas que estão nessa peça. Os iteradores personalizados são uma ótima ferramenta aqui e muito elegante de usar.
227 Roel