Tengo una clase que representa una lista de personas.
class AddressBook
{
public:
AddressBook();
private:
std::vector<People> people;
}
Quiero permitir que los clientes repitan el vector de las personas. El primer pensamiento que tuve fue simplemente:
std::vector<People> & getPeople { return people; }
Sin embargo, no quiero filtrar los detalles de implementación al cliente . Es posible que desee mantener ciertos invariantes cuando se modifica el vector, y pierdo el control sobre estos invariantes cuando pierdo la implementación.
¿Cuál es la mejor manera de permitir la iteración sin filtrar las partes internas?
begin()
yend()
son peligrosos porque (1) esos tipos son iteradores de vectores (clases) que evitan que uno cambie a otro contenedor como aset
. (2) Si se modifica el vector (p. Ej., Crecido o algunos elementos borrados), algunos o todos los iteradores del vector podrían haberse invalidado.Respuestas:
Permitir la iteración sin filtrar las partes internas es exactamente lo que promete el patrón iterador. Por supuesto, eso es principalmente teoría, así que aquí hay un ejemplo práctico:
Usted proporciona estándares
begin
yend
métodos, al igual que las secuencias en el STL, y los implementa simplemente reenviando al método vectorial Esto filtra algunos detalles de implementación, es decir, que está devolviendo un iterador de vectores, pero ningún cliente sensato debería depender de eso, por lo que no es una preocupación. He mostrado todas las sobrecargas aquí, pero por supuesto, puede comenzar simplemente proporcionando la versión constante si los clientes no pueden cambiar ninguna entrada de Personas. El uso de la nomenclatura estándar tiene beneficios: cualquiera que lea el código inmediatamente sabe que proporciona una iteración 'estándar' y, como tal, funciona con todos los algoritmos comunes, rango basado en bucles, etc.fuente
begin()
yend()
que simplemente reenviar a los vectoresbegin()
yend()
permite al usuario modificar los elementos en el vector en sí, tal vez utilizandostd::sort()
. Dependiendo de qué invariantes esté tratando de preservar, esto puede o no ser aceptable. Proporcionarbegin()
yend()
, sin embargo, es necesario para admitir el C ++ 11 basado en rango para bucles.Si la iteración es todo lo que necesita, entonces quizás sea
std::for_each
suficiente un envoltorio :fuente
const
iteración. Elfor_each()
es unaconst
función miembro. Por lo tanto, el miembropeople
es visto comoconst
. Por lo tanto,begin()
yend()
se sobrecargará comoconst
. Por lo tanto, volverán aconst_iterator
speople
. Por lo tanto,f()
recibirá aPeople const&
. Escribircbegin()
/cend()
aquí no cambiará nada, en la práctica, aunque como usuario obsesivo deconst
Yo podría argumentar que todavía vale la pena hacerlo, como (a) por qué no; es sólo 2 caracteres, (b) I como decir lo que quiero decir, al menos conconst
, (c) que protege contra accidentalmente pegar alguna parte noconst
, etc.Puede usar el idioma pimpl y proporcionar métodos para iterar sobre el contenedor.
En el encabezado:
En la fuente:
De esta manera, si su cliente utiliza el typedef del encabezado, no notará qué tipo de contenedor está utilizando. Y los detalles de implementación están completamente ocultos.
fuente
Se podrían proporcionar funciones miembro:
Lo que permite el acceso sin exponer detalles de implementación (como contigüidad) y usarlos dentro de una clase de iterador:
Los iteradores pueden ser devueltos por la libreta de direcciones de la siguiente manera:
Probablemente necesites completar la clase de iterador con rasgos, etc., pero creo que esto hará lo que has pedido.
fuente
si desea la implementación exacta de funciones de std :: vector, use la herencia privada como se muestra a continuación y controle lo que está expuesto.
Editar: Esto no se recomienda si también desea ocultar la estructura de datos interna, es decir, std :: vector
fuente
vector
demás? que nunca quiere usar, pero que de todos modos debe heredar?), y tal vez sea activamente peligroso (¿y si la clase de la que se hereda perezosamente podría eliminarse mediante un puntero a ese tipo base en alguna parte, pero [irresponsablemente] no protegió contra la destrucción de un obj derivado a través de dicho puntero, ¿así que simplemente destruirlo es UB?)