2011-06-23 18 views
5
vector<Foo*>&   getVectorOfFoo(); 

quiero proveer una lista de mis objetos Foo a otros. ¿Es esta la mejor manera de hacerlo? Estoy devolviendo una referencia aquí y no copiando correctamente?Cómo devolver correctamente un gran vector de

La persona que llama podría (accidentalmente) modificar esta lista ¿no? ¿Hay alguna manera de evitar esta posibilidad? ¿Podría devolver el vector const? Pero ellos siempre pueden modificar los objetos Foo y no hay mucho que pueda hacer allí. 10-20 personas diferentes escribirán código que use esta lista de Foo.

+2

Puede devolver la referencia const al vector. De esa manera, la persona que llama no puede modificarlo. – Juho

+2

Parece claro de la discusión y responde que estaba implícito que getVectorOfFoo() es una función miembro de una clase y que el vector es una variable miembro de la clase, pero esto no se indicó en ninguna parte de la pregunta, y hace la diferencia . –

+0

Buena discusión, aprendí algo de todos. Gracias chicos. – Mark

Respuesta

9

Primera no lo hacen devolver una lista de punteros.
Eso lo hace doblemente confuso sobre las acciones permitidas.

Boost tiene una solución (como de costumbre).
Devuelve un contenedor de puntero. Esto expone los punteros como miembros normales.

boost::ptr_vector<Foo> const& getVectorOfFoo(); 

Ahora el usuario no puede alterar el vector devuelto.

Ejemplo:

#include <boost/ptr_container/ptr_vector.hpp> 

class Foo 
{ 
    public: 
     void plop()   {} 
     void poop() const {} 
}; 

boost::ptr_vector<Foo> const& getVectorOfFoo() 
{ 
    static boost::ptr_vector<Foo> instance; // Create and fill container with FOO objects. 
    instance.push_back(new Foo); 
    return instance; 
} 

int main() 
{ 
    boost::ptr_vector<Foo> const& value = getVectorOfFoo(); 

    value[0].plop(); // Fail. not a const method (comment out this line) 
    value[0].poop(); 
} 
+0

+1, nunca antes había pensado en esta aplicación de los contenedores de puntero. –

+0

@MarkB: Yo tampoco. Me gusta esto –

1

devolverlo como const también.

const vector<Foo *> &getVectorOfFoo(); 
1

Como ya se ha dicho, proporcionar acceso const al recipiente.

Pero aún no es "perfecto" porque necesita exponer el contenedor al mundo, por lo que si lo cambia, también obliga al código de usuario a cambiar si la interfaz ya no es la misma.

Pero hay una última esperanza:

Si puede utilizar lambdas (C++ 0x), entonces es mejor que proporcionan un algoritmo for_each:

class MyThingManager 
{ 
public: 

    template< typename FunctorType > // note : could be non-template, in wich case use std::function<> 
    void modify_each_thing(FunctorType f) // if you use std::function, you can put the implementation in the cpp file instead of the header/inline 
    { 
     // do some checks, or maybe generate a list of Things that you allow to modify 

     // then apply the function (here we assume we don't have a separate list - implementation defined for the win!) 
     std::for_each(m_things.begin(), m_things.end(), f); // oh yeah 
     // then we can apply anything more we want 
     check_everything_is_still_valid(); 
     notify_the_world(); 
    } 

    // here is a simpler read-only version 
    template< typename FunctorType > 
    void for_each_thing(FunctorType f) const { std::for_each(m_things.begin(), m_things.end(), f); } 


    // in case you want the user to know how many things to manipulate 
    size_t things_count() const { return m_things;} 



private: 

    std::vector<Thing> m_things; // could be any container, that's isolated from the algorithm! 

}; 

Uso:

MyThingManager manager; 
manager.for_each_thing([](const Thing& thing){ std::cout << "\nA thing : " << thing; }); 
+1

Relacionados: http://en.wikipedia.org/wiki/Visitor_pattern – luke

2

La incorporación del tipo de devolución del contenedor en la firma del método evitará que se cambie el tipo de contenedor subyacente en caso de que un contenedor alternativo sea más apropiado. en el futuro.

Al menos debe considerar el uso de typedef para ocultar el tipo de contenedor real, y documentar las capacidades mínimas del objeto de devolución en lugar de devolver un vector directamente.

Pero podría considerar proporcionar una interfaz de iterador, algo así como YourThing::const_iterator getFooBegin() y getFooEnd(). De esta forma, el código del cliente no puede modificar el contenedor subyacente O los objetos, y de hecho ni siquiera necesita saber cuál es el tipo de contenedor, lo que permite una mayor flexibilidad en el futuro.

Si prefiere devolver un contenedor, debe decidir con precisión cuál es la semántica, a partir de la pregunta que parece que desea que sea de solo lectura. En ese caso, tendría que hacer una copia del contenedor en otro vector que contendría punteros const en lugar de non-const, por lo que el cliente no podría modificarlos. Alternativamente, otra respuesta proporcionó una muy buena sugerencia para usar contenedores impulsores de impulso.

Cuestiones relacionadas