Fiz uma coleção para a qual desejo fornecer um iterador de acesso aleatório no estilo STL. Eu estava procurando um exemplo de implementação de um iterador, mas não encontrei nenhum. Eu sei sobre a necessidade de sobrecargas const []
e *
operadores. Quais são os requisitos para um iterador ser "estilo STL" e quais outras armadilhas devem ser evitadas (se houver)?
Contexto adicional: é para uma biblioteca e não quero introduzir nenhuma dependência a menos que realmente precise. Eu escrevo minha própria coleção para poder fornecer compatibilidade binária entre C ++ 03 e C ++ 11 com o mesmo compilador (portanto, nenhum STL provavelmente quebraria).
c++
iterator
const-iterator
Tamás Szelei
fonte
fonte
Respostas:
http://www.cplusplus.com/reference/std/iterator/ possui um gráfico útil que detalha as especificações do § 24.2.2 do padrão C ++ 11. Basicamente, os iteradores têm tags que descrevem as operações válidas e as tags possuem uma hierarquia. Abaixo, é puramente simbólico, essas classes não existem realmente como tais.
Você pode se especializar
std::iterator_traits<youriterator>
ou colocar os mesmos typedefs no próprio iterador ou herdarstd::iterator
(que possui esses typedefs). Prefiro a segunda opção, para evitar alterar as coisas nostd
espaço para nome e para facilitar a leitura, mas a maioria das pessoas herdastd::iterator
.Observe o iterator_category deve ser um dos
std::input_iterator_tag
,std::output_iterator_tag
,std::forward_iterator_tag
,std::bidirectional_iterator_tag
, oustd::random_access_iterator_tag
, dependendo de quais requisitos seus iteradoras satisfaz. Dependendo do seu iterador, você pode optar por se especializarstd::next
,std::prev
,std::advance
, estd::distance
, bem como, mas isso raramente é necessário. Em casos extremamente raros , você pode querer se especializarstd::begin
estd::end
.Seu contêiner provavelmente também deve ter um
const_iterator
iterador (possivelmente mutável) para dados constantes semelhantes ao seu,iterator
exceto que ele deve ser implicitamente construtível a partir de ae ositerator
usuários não devem poder modificar os dados. É comum que seu ponteiro interno seja um ponteiro para dados não constantes e tenhaiterator
herdado deconst_iterator
modo a minimizar a duplicação de código.Minha postagem em Writing your own STL Container possui um protótipo de contêiner / iterador mais completo.
fonte
std::iterator_traits
ou definir os typedefs, você também pode derivar destd::iterator
, que os define, dependendo dos parâmetros do modelo.const_iterator
. O que mais estava faltando no meu post? Você parece sugerir que há mais para adicionar à classe, mas a pergunta é especificamente sobre a implementação de iteradores.std::iterator
foi proposto como obsoleto em C ++ 17 ; não era, mas eu não acreditaria nisso por muito mais tempo.std::iterator
foi descontinuada.operator bool
é incrivelmente perigoso. Alguém tentará usar isso para detectar o final de um intervalowhile(it++)
, mas tudo o que realmente verifica é se o iterador foi construído com um parâmetro.A documentação iterator_facade do Boost.Iterator fornece o que parece ser um bom tutorial sobre como implementar iteradores para uma lista vinculada. Você poderia usar isso como ponto de partida para criar um iterador de acesso aleatório sobre seu contêiner?
Se nada mais, você pode dar uma olhada nas funções-membro e nos typedefs fornecidos por
iterator_facade
e usá-los como ponto de partida para criar seus próprios.fonte
Thomas Becker escreveu um artigo útil sobre o assunto aqui .
Também havia essa abordagem (talvez mais simples) que apareceu anteriormente no SO: Como implementar corretamente iteradores e const_iterators personalizados?
fonte
Aqui está uma amostra do iterador de ponteiro bruto.
Você não deve usar a classe iterator para trabalhar com ponteiros brutos!
Solução de loop com base no intervalo do ponteiro bruto. Por favor, corrija-me, se houver uma maneira melhor de fazer um loop baseado em intervalo a partir do ponteiro bruto.
E teste simples
fonte
Primeiro, você pode procurar aqui uma lista das várias operações que os tipos de iteradores individuais precisam oferecer suporte.
Em seguida, quando você tiver criado sua classe de iterador, precisará se especializar
std::iterator_traits
e fornecer algunstypedef
s necessários (comoiterator_category
ouvalue_type
) ou derivá-lo alternativamentestd::iterator
, que define ostypedef
s necessários para você e, portanto, pode ser usado com o padrãostd::iterator_traits
.isenção de responsabilidade: sei que algumas pessoas não gostam
cplusplus.com
muito, mas fornecem algumas informações realmente úteis sobre isso.fonte
Eu estava no mesmo barco que você por diferentes razões (em parte educacionais, em parte restrições). Eu tive que reescrever todos os contêineres da biblioteca padrão e os contêineres tiveram que estar em conformidade com o padrão. Isso significa que, se eu trocar meu contêiner pela versão stl , o código funcionará da mesma maneira. O que também significava que eu tinha que reescrever os iteradores.
Enfim, eu olhei para o EASTL . Além de aprender muito sobre contêineres que eu nunca aprendi esse tempo todo usando os contêineres stl ou durante meus cursos de graduação. A principal razão é que o EASTL é mais legível do que o equivalente do stl (eu achei isso simplesmente devido à falta de todas as macros e ao estilo de codificação direto). Existem algumas coisas nojentas (como #ifdefs para exceções), mas nada para sobrecarregá-lo.
Como outros mencionados, consulte a referência da cplusplus.com sobre iteradores e contêineres.
fonte
Eu estava tentando resolver o problema de conseguir iterar em várias matrizes de texto diferentes, todas armazenadas em um banco de dados residente na memória que é grande
struct
.O seguinte foi elaborado usando o Visual Studio 2017 Community Edition em um aplicativo de teste do MFC. Estou incluindo isso como um exemplo, pois essa postagem foi uma das várias que encontrei, fornecendo alguma ajuda, mas ainda assim insuficiente para minhas necessidades.
O
struct
contendo os dados residentes da memória era semelhante ao seguinte. Eu removi a maioria dos elementos por questões de brevidade e também não incluí as definições de pré-processador usadas (o SDK em uso é para C e C ++ e é antigo).O que eu estava interessado em fazer é ter iteradores para as várias
WCHAR
matrizes bidimensionais que continham cadeias de texto para mnemônicos.A abordagem atual é usar um modelo para definir uma classe de proxy para cada uma das matrizes e, em seguida, ter uma única classe de iterador que pode ser usada para iterar sobre uma matriz específica usando um objeto proxy que representa a matriz.
Uma cópia dos dados residentes da memória é armazenada em um objeto que lida com a leitura e gravação dos dados residentes da memória de / para o disco. Esta classe
CFilePara
contém a classe de proxy com modelo (MnemonicIteratorDimSize
e a subclasse da qual é derivadaMnemonicIteratorDimSizeBase
) e a classe do iteradorMnemonicIterator
,.O objeto proxy criado é anexado a um objeto iterador que acessa as informações necessárias por meio de uma interface descrita por uma classe base da qual todas as classes proxy são derivadas. O resultado é ter um único tipo de classe de iterador que pode ser usado com várias classes de proxy diferentes, porque as diferentes classes de proxy expõem a mesma interface, a interface da classe base do proxy.
A primeira coisa foi criar um conjunto de identificadores que seriam fornecidos a uma fábrica de classes para gerar o objeto proxy específico para esse tipo de mnemônico. Esses identificadores são usados como parte da interface do usuário para identificar os dados de provisionamento específicos que o usuário está interessado em ver e possivelmente modificar.
A Classe Proxy
A classe de proxy modelada e sua classe base são as seguintes. Eu precisava acomodar vários tipos diferentes de
wchar_t
matrizes de string de texto. As matrizes bidimensionais tinham números diferentes de mnemônicos, dependendo do tipo (objetivo) do mnemônico e os diferentes tipos de mnemônicos tinham diferentes comprimentos máximos, variando entre cinco caracteres e vinte caracteres. Os modelos para a classe de proxy derivada eram um ajuste natural ao modelo que requer o número máximo de caracteres em cada mnemônico. Após a criação do objeto proxy, usamos oSetRange()
método para especificar a matriz mnemônica real e seu intervalo.A classe Iterator
A própria classe de iterador é a seguinte. Essa classe fornece apenas a funcionalidade básica do iterador direto, que é tudo o que é necessário no momento. No entanto, espero que isso mude ou seja estendido quando precisar de algo adicional.
A fábrica de objetos proxy determina qual objeto será criado com base no identificador mnemônico. O objeto proxy é criado e o ponteiro retornado é o tipo de classe base padrão para ter uma interface uniforme, independentemente de quais das diferentes seções mnemônicas estão sendo acessadas. O
SetRange()
método é usado para especificar para o objeto proxy os elementos específicos da matriz que o proxy representa e o intervalo dos elementos da matriz.Usando a classe de proxy e o iterador
A classe proxy e seu iterador são usados como mostrado no loop a seguir para preencher um
CListCtrl
objeto com uma lista de mnemônicos. Estou usandostd::unique_ptr
para que, quando a classe proxy não seja mais necessária e astd::unique_ptr
saída do escopo, a memória seja limpa.O que esse código-fonte faz é criar um objeto proxy para a matriz dentro do
struct
que corresponde ao identificador mnemônico especificado. Em seguida, ele cria um iterador para esse objeto, usa um intervalofor
para preencher oCListCtrl
controle e depois limpa. Essas são todas aswchar_t
strings de texto bruto que podem ser exatamente o número de elementos da matriz, por isso copiamos a string em um buffer temporário para garantir que o texto seja zero terminado.fonte
E agora um iterador de chaves para loop for baseado em intervalo.
Uso:
Era isso que eu estava procurando. Mas ninguém teve, ao que parece.
Você recebe meu alinhamento do código do TOC como um bônus.
Como exercício, escreva o seu para
values(my_map)
fonte