2009-06-29 13 views
9

Vengo del mundo de Java y estoy construyendo un pequeño programa de C++ en este momento. Tengo un objeto que hace algún trabajo y luego devuelve el resultado del trabajo como una lista.Interfaz/Superclase para Colecciones/Contenedores en C++

Ahora, un día más tarde he cambiado el comportamiento del objeto para guardar los resultados en un conjunto para evitar duplicados en el contenedor. Pero no puedo simplemente devolver el conjunto porque utilicé una lista para la interfaz la primera vez. ¿Existe una interfaz de contenedor común que pueda usar para especificar la interfaz de mi objeto y olvidar el tipo de contenedor que uso internamente?

Por el momento estoy creando un conjunto de añadir todos los valores y luego crear una lista del conjunto:

return std::list<foo>(this->mySet.begin(), this->mySet.end()) 

parece un poco extraño.

Respuesta

9

El concepto de contenedor está engendrado por iteradores.
Como has visto, la codificación dura de un tipo específico de contenedor probablemente no sea lo que deseas. Entonces haz que tu clase devuelva iteradores. Luego puede reutilizar los iteradores de conatiners.

class MyClass 
{ 
    private: 
     typedef std::list<int>   Container; 
    public: 
     typedef Container::iterator  iterator; 
     typedef Container::const_iterator const_iterator; 


     iterator  begin()  {return myData.begin();} 
     const_iterator begin() const {return myData.begin();} 

     iterator  end()   {return myData.end();} 
     const_iterator end() const {return myData.end();} 

    private: 
     Container myData; 
}; 

Ahora, cuando se cambia el tipo de contenedores de std :: lista para std :: set nadie necesita saber.
También al usar los nombres estándar que otros contenedores usan su clase comienza a verse como cualquier otro contenedor de la STL.

Nota: Un método que devuelve un const_iterator debe ser un método const.

9

El conjunto C++ - biblioteca estándar incluyendo sus contenedores es - a diferencia de Java - no interfaz (herencia, polimorfismo) - pero (en aras de la eficiencia) basado en la plantilla.

Se puede crear un envoltorio polimórfica alrededor de su colección, pero este no es el C++ - camino.

La solución más simple es sólo para simplificar el Programm con algunas alias de tipo:

#include <iostream> 
#include <list> 
#include <vector> 

using namespace std; 

class Test { 

private: 
    typedef vector<int> Collection; 

    Collection c; 

public: 

    typedef Collection::const_iterator It; 

    void insert(int Item) { 
     c.push_back(Item); 
    } 

    It begin() const { return c.begin(); } 
    It end() const { return c.end(); } 

}; 

int main() { 

    Test foo; 

    foo.insert(23); 
    foo.insert(40); 

    for (Test::It i = foo.begin(); i != foo.end(); ++i) 
     cout << *i << endl; 

    return 0; 
} 

Ahora se puede cambiar el Collection -typedef sin tener que cambiar nada más. (Nota: Si hace público Collection, el usuario podrá referir el tipo que utilizó explícitamente)

+0

Si le devuelven const_iterators. Luego haga los métodos const begin() y end(). –

+0

Sí - Editado eso – Dario

2

De su descripción, creo que la respuesta corta es no.

En general, cuando estoy creando algún tipo de colección como esta normalmente me gustaría utilizar un typedef para especificar el contenedor que estoy usando:

class Object { 
    typedef std::list<int> Cont; 
    typedef Cont::iterator iterator; 
    typedef Cont::const_iterator const_iterator; 

    // .... 
}; 

Todo el código de cliente se refiere a "Object :: Cont "etc. y siempre y cuando los clientes solo utilicen las características generales de los contenedores, no necesitarán cambiar si el contenedor cambia.

Si no puede cambiar su API ahora, entonces creo que su solución es bastante buena, sin embargo, dependiendo de los datos que tenga, si hace muchos insertos que tienden a ser únicos, entonces puede ser más eficiente para continuar utilizando la lista y solo eliminar duplicados al final:

void foo (std::list<int> & list) { 

    // ... fill the list 

    list.sort(); 
    list.unique(); 
} 
2

No existe interfaz. En cambio, normalmente usaría plantillas, y simplemente diría "No me importa de qué tipo es, siempre que se comporte como un contenedor".

Asumiendo que su función se ve así:

std::list<int> DoStuff() 

se le puede llamar así:

template <typename container_type> 
void caller() { 
    container_type result = DoStuff(); 
} 

Sólo la primera función tiene que ser cambiado si decide devolver un set lugar. La función de llamada realmente no le importa (siempre y cuando no confíe en los detalles de una lista, por supuesto).

Si publica un código de muestra un poco más, podríamos sugerir mejor cómo se debe hacer en C++.

Cuestiones relacionadas