Pergunta

Normalmente você vai encontrar o código STL como esta:

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

Mas nós realmente temos a recomendação para escrevê-lo como este:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
for (; Iter != IterEnd; ++Iter)
{
}

Se você está preocupado com o escopo, adicione anexando chaves:

{
    SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
    SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
    for (; Iter != IterEnd; ++Iter)
    {
    }
}

Esta é suposto dar um ganho de velocidade e eficiência, especialmente se você estiver consolas de programação, porque a função .end () não é chamado em cada iteração do loop. Eu só tomar a melhoria de desempenho para concedido, soa razoável, mas eu não sei o quanto e certamente depende do tipo de recipiente e implementação STL real em uso. Mas ter usado este estilo para um par de meses agora eu realmente prefiro-o sobre o primeiro de qualquer maneira.

A razão de ser a legibilidade: o para a linha é limpo e arrumado. Com qualificadores e variáveis ??de membro em código de produção real, é muito fácil ter realmente longa para as linhas se você usar o estilo no primeiro exemplo. É por isso que intencionalmente o fizeram para ter uma barra de rolagem horizontal neste exemplo, só assim você vê o que eu estou falando. ;)

Por outro lado, de repente você introduzir as variáveis ??Iter ao âmbito externo do loop for. Mas então, pelo menos no ambiente de trabalho i no, o Iter teria sido acessíveis no âmbito externo, mesmo no primeiro exemplo.

Qual é a sua opinião sobre isso? Há algum do Pro para o primeiro estilo diferente possivelmente limitando o escopo do Iter?

Foi útil?

Solução

Se você quebrar seu código em linhas corretamente, o formulário em linha seria igualmente legível. Além disso, você deve sempre fazer o iterEnd = container.end() como uma otimização:

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
    IterEnd = m_SomeMemberContainerVar.end();
    Iter != IterEnd;
    ++Iter)
{
}

Atualização:. Fixou o código pelo conselho de paercebal

Outras dicas

Outra alternativa é usar uma macro foreach, por exemplo boost foreach :

BOOST_FOREACH( ContainedType item, m_SomeMemberContainerVar )
{
   mangle( item );
}

Eu sei macros são desencorajados em c ++ moderno, mas até que a palavra-chave auto é amplamente disponível esta é a melhor maneira que eu encontrei para obter algo que é conciso e legível, e ainda completamente typesafe e rápido. Você pode implementar sua macro usando qualquer estilo de inicialização você recebe um melhor desempenho.

Há também uma nota na página vinculada sobre redefinindo BOOST_FOREACH como foreach para evitar o irritante todos os tampões.

A primeira forma (para dentro do laço) é melhor se a iteração não é necessária depois de o loop. Ela limita o seu alcance para o loop for.

Eu duvido seriamente que haja qualquer ganho de eficiência de qualquer maneira. Ele também pode ser feita mais legível com um typedef.

typedef SomeClass::SomeContainer::iterator MyIter;

for (MyIter Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

Eu recomendaria nomes mais curtos; -)

Tendo visto isso em g ++ em -O2 otimização (apenas para ser mais específico)

Não há nenhuma diferença no código gerado para std :: vector, std :: lista e std :: map (e amigos). Há uma pequena sobrecarga com std :: deque.

Assim, em geral, do ponto de vista de desempenho que faz pouca diferença.

Não, não é uma má idéia para obter um porão em iter.end() antes do laço começar. Se o seu ciclo muda o recipiente, em seguida, o fim iterador pode ser invalidada. Além disso, o método end() é garantida para ser O (1).

A optimização prematura é a raiz de todo o mal.

Além disso, o compilador pode ser mais inteligente do que você pensa.

Eu não tenho um particularmente forte forma de parecer um ou o outro, embora iterador vida me inclinar-se para a versão para-escopo.

No entanto, a leitura pode ser um problema; que pode ser ajudado usando um typedef de modo que o tipo de iterador é um pouco mais administrável:

typedef SomeClass::SomeContainer::iterator sc_iter_t;

for (sc_iter_t Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

Não é um enorme melhoria, mas um pouco.

Eu não tenho nenhuma experiência com o console, mas na maioria dos compiliers modernas C ++ ou outra opção acaba sendo equivilent exceto para a questão do escopo. O compilier visual studio vai quase sempre, mesmo em código de depuração colocar a comparação condição em uma variável temporária implícita (geralmente um registo). Assim, enquanto logicamente parece que o end () chamada é feita através de cada iteração, o código compilado otimizado realmente só faz a chamada de uma vez a comparação é a única coisa que é feito cada vez subsiquent através do laço.

Este não pode ser o caso em consoles, mas você poderia unassemble o loop para verificar se a otimização está ocorrendo. Se for, então você pode você o que estilo você prefere ou é padrão na sua organização.

Pode fazer para o código desarticulada, mas também gosto de puxá-lo para fora para uma função separada, e passar os dois iteradores para ele.

doStuff(coll.begin(), coll.end())

e ter ..

template<typename InIt>
void doStuff(InIt first, InIt last)
{
   for (InIt curr = first; curr!= last; ++curr)
   {
       // Do stuff
   }
 }

Encontros gostar:

  • Nunca tenho que mencionar o tipo iterator feio (ou pensar sobre se é const ou não-const)
  • Se não houver ganho de não chamar end () em cada iteração, eu estou recebendo-lo

As coisas não como:

  • rompe-se o código
  • aéreo de chamada de função adicional.

Mas um dia, teremos lambdas!

Eu não acho que é mau estilo em tudo. Basta usar typedefs para evitar a verbosidade STL e longas filas.

typedef set<Apple> AppleSet;
typedef AppleSet::iterator  AppleIter;
AppleSet  apples;

for (AppleIter it = apples.begin (); it != apples.end (); ++it)
{
   ...
}

Spartan Programação é uma maneira de mitigar as suas preocupações de estilo.

Você pode jogar chaves em todo o inicialização e loop se você está preocupado com escopo. Muitas vezes o que eu vou fazer é iterators declarar, no início da função e reutilizá-los ao longo do programa.

Eu concordo com Ferruccio. O primeiro estilo pode ser preferido por alguns, a fim de puxar o end () chamar para fora do loop.

Eu também poderia acrescentar que C ++ 0x vai realmente fazer as duas versões muito mais limpo:

for (auto iter = container.begin(); iter != container.end(); ++iter)
{
   ...
}

auto iter = container.begin();
auto endIter = container.end();
for (; iter != endIter; ++iter)
{
   ...
}

Eu costumo escrever:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
                                   IterEnd = m_SomeMemberContainerVar.end();

for(...)

Acho que a segunda opção mais legível, como você não acabar com uma linha gigante. No entanto, Ferruccio traz um ponto sobre o alcance bom.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top