2010-09-28 12 views
11

Por eso, cuando tenemos que atravesar un contenedor desde el principio hasta el final que escribir algo como¿Cómo se implementa end() en contenedores STL?

for (i = v->begin(); i != v->end(); i++)

asumiendo i es un iterador para el envase v.

Mi pregunta es "¿qué garantiza que el final siempre apuntará a uno pasado el último elemento en el contenedor?" ¿Cómo garantiza STL este comportamiento y existe la posibilidad de que este caso no sea cierto?

+4

El STL no garantiza este comportamiento. El STL ** implementa ** este comportamiento en función de los requisitos definidos por el estándar. El estándar dice que así es como se supone que debe funcionar el desarrollador que implementa el STL y se supone que debe hacer que el STL funcione correctamente. –

+5

Eso no es una buena práctica. Prefiera el operador de preincremento ++ i en lugar de i ++ cuando no está almacenando el valor. Para muchos tipos, es más rápido. –

+0

@Jive Dadson: esa no es mi principal preocupación -> volver a calcular 'v-> end()' en cada vuelta del ciclo ciertamente es menos eficiente ... 'for (auto it = v.begin(), end = v .end(); it! = end; ++ it) 'es la forma canónica, aunque en C++ 0x uno también podría usar' for (auto val: v) 'o' std :: foreach' y un lambda función. –

Respuesta

3

La especificación stl garantiza que el final será uno pasado el final See here. Ese siempre será el caso. Exactamente cómo lo hace puede depender de la implementación (a veces los valores simplemente se configuran como nulos, por ejemplo), pero tenga la seguridad de que su ciclo estará bien siempre que v sea un puntero válido.

1

"end siempre señalará uno pasado el último elemento en el contenedor" significa que si incrementa el iterador que apunta al último elemento, será igual al resultado de end(). La implementación puede ser diferente. En Visual C++ std::vector::end() devuelve un iterador específico de implementación que contiene cero puntero.

+2

Lo dudo mucho, ya que 'std :: vector :: iterator' es un iterador de acceso aleatorio. Si 'std :: vector :: end()' devuelto '(T *) 0',' end() - begin() 'sería ilegal, pero debe devolver' std :: vector :: size() ' – MSalters

+1

Para ser más correctos:' end' devuelve el iterador específico de implementación que contiene el puntero cero. –

+1

La parte de * que sostiene el puntero cero * es, bueno, innecesaria e imprecisa. En la implementación del vector gcc, 'vector <> :: end()' se define como 'vector <> :: begin() + vector <> :: size()', y se almacena en el objeto 'vector <>' (El vector GCC se implementa por medio de tres punteros: * begin *, * end * y * end_of_capacity *, donde 'begin == end' si el vector está vacío,' begin + size() == end' en todo momento y 'end == end_of_capacity' if' size() == capacity() '. No hay * puntero cero * en ningún lado. –

3

C++ 03 Sección 23.1/7 dice

begin() devuelve un iterador en referencia al primer elemento en el recipiente.

end() devuelve un iterador que es el valor de pasado para el contenedor.

Si el contenedor está vacío, entonces begin() == end();

+0

¿Es esta la razón por la que la slist no es oficialmente STL? El final() de slist es 0 – vrdhn

21

STL asegura este comportamiento mediante el almacenamiento siempre cosas como esta:

vector

Al final (juego de palabras), no importa lo que end()es, siempre y cuando sea siempre end() (y, obviamente, no se puede confundir con ningún otro nodo).

+13

Mate, obtienes +1 solo por los gráficos funky: -) – paxdiablo

+1

Pintar FTW, yo diría :) –

+0

¿La cita debajo del estándar de C++ no invalida esta respuesta? especialmente el gráfico. – yasouser

1

Usted está preguntando sobre todos los contenedores STL ... ni una sola mención de vector específicamente donde end() podría implementarse como usted intuitivamente espera. ¿Qué es una pasada en un std :: map <>? El hecho de que "el final es uno más allá del último nodo usado" es simplemente un concepto lógico, que expresa que puede incrementar de manera segura desde ese último nodo utilizado, diferenciarlo/equipararlo del concepto abstracto de "fin" y hacer algún nodo aritmética donde se considera que el extremo es uno más adelante que el último nodo utilizado. No lo tomes demasiado literalmente.

0

Como algunos de los carteles anteriores han declarado end() es uno pasado el elemento final. Si necesita acceder al último elemento a través de iteradores, use iter = container.end() - 1;. De lo contrario, en el caso de vectores, variable = someVector.back();. Suponiendo que la variable es del tipo de datos someVector contiene.

En cuanto a lo que garantiza que apunta hacia el final, el contenedor maneja eso internamente.Solo tienes que tratarlo como un blackbox como cualquier otro objeto y confiar en que lo hace correctamente.

Cada vez que se cambia el tamaño del contenedor, este rastreará dónde está el extremo y estará actualizado antes de acceder end() nuevamente. Dependiendo del contenedor, sin embargo, si tiene un iterador y lo altera de alguna manera, puede invalidar el iterador y romper su proceso de iteración.

Cuestiones relacionadas