1) Devolver un iterador es una buena idea. Cuando hace esto, la forma natural de indicar el caso "no encontrado" es devolver el iterador .end(). Esto tiene el inconveniente de que la abstracción es un poco permeable: la persona que llama tiene que ser capaz de obtener este valor .end() para compararlo con la comprobación de errores, y el iterador devuelto expone una interfaz más rica de lo que Me gusta (el código del cliente no debería jugar con aumentar y disminuir el iterador).
2) Devolver un vector vacío es tan simple como crear un vector vacío y devolverlo. Creando un vector vacío = construyendo un vector que está vacío. Esto es lo que obtienes de la lista de percusión: el constructor predeterminado de la clase de vector.
3) No necesita, y no debe, implementar el bucle de búsqueda usted mismo. La biblioteca estándar ya implementa esto para usted. (Hay una find
función especializada para map
s debido a la diferencia clave/valor. Para secuencias como list
, vector
y deque
, prefieren la función libre std::find
, que viene de <algorithm>
.
4) Usted debe preferir a aceptar la función parámetros (cuando son instancias de clases, como std::string
) y datos de retorno (especialmente cosas complejas como un vector de cadenas) por referencia. Pasar y regresar por valor implica una copia; a veces el compilador puede optimizar esto, pero no es tan confiable como nos gustaría.Además, la razón por la que estás usando C++ en primer lugar es tener ese nivel de control sobre las cosas, ¿verdad? Si no, entonces no te tortures con eso.
Sin embargo, no puede hacer eso si va a devolver un valor recién creado alguna vez. Otra forma de diseñar la interfaz es devolver un puntero al vector de cadenas (tenga en cuenta que la aritmética del puntero en estos no será válida) dentro del mapa, o un puntero NULL si no se encuentra el valor. Esto evita la copia y distingue un resultado "no encontrado" de un vector vacío real dentro de los datos, pero significa que el código del cliente tiene que lidiar con un puntero crudo icky.
5) Tener 'retorno' en el nombre de una función es inútil, ya que devolver es lo que hacen las funciones. OTOH, es una buena idea nombrar las cosas de una manera que haga evidente por qué los parámetros son lo que son.
6) Con los iteradores para tipos complejos, a menudo es una buena idea configurar los tipos.
Volviendo un iterador es tan simple como:
typedef map<string, vector<string> >::iterator graph_iterator;
graph_iterator edges_named(const string& node_name) {
return outgoing.find(node_name);
}
La devolución de un vector de cadenas es tan simple como:
typedef map<string, vector<string> >::iterator graph_iterator;
vector<string> edges_named(const string& node_name) {
graph_iterator it = outgoing.find(node_name);
return it == outgoing.end() ? vector<string>() : it->second;
}
devolviendo un puntero es tan simple como:
typedef map<string, vector<string> >::iterator graph_iterator;
vector<string>* edges_named(const string& node_name) {
graph_iterator it = outgoing.find(node_name);
return it == outgoing.end() ? NULL : &(it->second);
}
Elige sabiamente.
Me siento un poco mareado por esos indicadores crudos. Quizás ese diseño podría ser útil, pero de alguna manera debería haber una mejor manera. El OP está invitado a explicar sus objetivos; tal vez podemos sugerir algo específico. –
@Karl, Awesome awesome answer. Creo que estos son los problemas que muchas personas me quieren, que vienen de otros idiomas a C++ y que no pueden usarlo eficientemente/efectivamente. – Anon
La conversión de iteradores a punteros crudos es una manera de restringir la interfaz. Sin embargo, todavía no evita el incremento/la disminución: solo significa que se romperá (el comportamiento indefinido que ** se espera ** se bloquee en circunstancias normales, pero quién sabe realmente) en lugar de dejar que el usuario rompa la encapsulación en itty piezas pequeñas Probablemente una mejor idea es crear una clase contenedora, una especie de puntero inteligente deliberadamente tonto. :) –