que libreria iterador español c++ pointers vector iterator gotw

libreria - stl c++ español



Para un vector, ¿por qué prefiere un iterador sobre un puntero? (3)

En When Is a Container Not a Container? Herb Sutter When Is a Container Not a Container? , muestra un ejemplo de tomar un puntero en un contenedor:

// Example 1: Is this code valid? safe? good? // vector<char> v; // ... char* p = &v[0]; // ... do something with *p ...

Luego lo sigue con una "mejora":

// Example 1(b): An improvement // (when it''s possible) // vector<char> v; // ... vector<char>::iterator i = v.begin(); // ... do something with *i ...

Pero en realidad no proporciona un argumento convincente:

En general, no es una mala guía preferir el uso de iteradores en lugar de punteros cuando se quiere apuntar a un objeto que está dentro de un contenedor. Después de todo, los iteradores se invalidan casi siempre en los mismos tiempos y de la misma forma que los punteros, y una razón por la que existen los iteradores es para proporcionar una forma de "apuntar" a un objeto contenido. Entonces, si tiene una opción, prefiere usar iteradores en contenedores.

Desafortunadamente, no siempre se puede obtener el mismo efecto con iteradores que con punteros dentro de un contenedor. Hay dos posibles inconvenientes principales para el método del iterador, y cuando se aplica tenemos que seguir usando punteros:

  1. No siempre puede usar convenientemente un iterador donde puede usar un puntero. (Ver ejemplo a continuación)

  2. El uso de iteradores puede incurrir en gastos adicionales de espacio y rendimiento, en los casos en que el iterador es un objeto y no solo un puntero.

En el caso de un vector, el iterador es solo un RandomAccessIterator. Para todos los efectos, se trata de una capa delgada sobre un puntero. Una implementación incluso reconoce esto:

// This iterator adapter is ''normal'' in the sense that it does not // change the semantics of any of the operators of its iterator // parameter. Its primary purpose is to convert an iterator that is // not a class, e.g. a pointer, into an iterator that is a class. // The _Container parameter exists solely so that different containers // using this template can instantiate different types, even if the // _Iterator parameter is the same.

Además, la implementación almacena un valor de miembro de tipo _Iterator , que es pointer o T* . En otras palabras, solo un puntero. Además, el tipo de difference_type para dicho tipo es std::ptrdiff_t y las operaciones definidas son simplemente envoltorios delgados (es decir, operator++ es ++_pointer , operator* is *_pointer ) y así sucesivamente.

Siguiendo el argumento de Sutter, esta clase de iterador no proporciona beneficios sobre punteros, solo inconvenientes. ¿Estoy en lo correcto?


No siempre puede usar convenientemente un iterador donde puede usar un puntero

Esa no es una de las desventajas. A veces es demasiado "conveniente" hacer que el puntero pase a lugares en los que realmente no quería que se fuera. Tener un tipo separado ayuda a validar los parámetros.

Algunas implementaciones tempranas usaban T* para vector :: iterator, pero causaba varios problemas, como que las personas pasaran accidentalmente un puntero no relacionado a las funciones vectoriales miembro. O asignando NULL al iterador.

El uso de iteradores puede incurrir en gastos adicionales de espacio y rendimiento, en los casos en que el iterador es un objeto y no solo un puntero.

Esto fue escrito en 1999, cuando también creímos que el código en <algorithm> debería optimizarse para diferentes tipos de contenedores. No mucho después todos se sorprendieron al ver que los compiladores se dieron cuenta de eso. ¡Los algoritmos genéricos que usan iteradores funcionaron bien!

Para un std :: vector, no hay absolutamente ningún espacio de tiempo para usar un iterador en lugar de un puntero. Descubrió que la clase de iterador es solo una capa delgada sobre un puntero. Los compiladores también verán eso y generarán un código equivalente.


Para vectores, en código no genérico, en su mayoría es correcto.

El beneficio es que puede pasar un RandomAccessIterator a un montón de algoritmos sin importar en qué contenedor itera el iterador, ya sea que ese contenedor tenga almacenamiento contiguo (y por lo tanto iteradores de puntero) o no. Es una abstracción.

(Esta abstracción, entre otras cosas, permite que las implementaciones cambien la implementación del puntero básico por algo un poco más sexy, como iteradores de rango comprobado para el uso de depuración).

En general, se considera que es un buen hábito utilizar iteradores a menos que realmente no pueda hacerlo. Después de todo, el hábito genera consistencia y la consistencia conduce a la capacidad de mantenimiento.

Los iteradores también se auto-documentan de una manera en que los punteros no lo son. ¿A qué se refiere un int* ? Ni idea. ¿A qué apunta un std::vector<int>::iterator ? Ajá ...

Finalmente, proporcionan una medida de seguridad de tipo, aunque tales iteradores pueden ser solo envoltorios delgados alrededor de punteros, no necesitan ser punteros: si un iterador es un tipo distinto en lugar de un alias de tipo, entonces no pasará accidentalmente su iterador en lugares que no quería que fuera, o configurándolo como "NULO" accidentalmente.

Estoy de acuerdo en que el argumento de Sutter es tan convincente como la mayoría de sus otros argumentos, es decir, no mucho.


Una razón de la vida real para preferir los iteradores sobre los punteros es que se pueden implementar como iteradores comprobados en compilaciones de depuración y ayudarlo a detectar algunos problemas desagradables con anticipación. Es decir:

vector<int>::iterator it; // uninitialized iterator it++;

o

for (it = vec1.begin(); it != vec2.end(); ++it) // different containers