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

Chcę podać listę moich obiektów Foo innym. Czy to najlepszy sposób na zrobienie tego? Zwracam tutaj odnośnik i nie kopiuję poprawnie?Jak prawidłowo zwrócić duży wektor

Czy dzwoniący mógłby (przypadkowo) zmodyfikować tę listę? Czy istnieje sposób na uniknięcie tej możliwości? Czy mogę zwrócić wektor const? Ale zawsze mogli także modyfikować obiekty Foo i niewiele mogłem zrobić. 10-20 różnych osób będzie pisać kod, który używa tej listy Foo.

+2

Możesz po prostu zwrócić const reference do wektora. W ten sposób dzwoniący nie może go zmodyfikować. – Juho

+2

Wygląda na to, że z dyskusji i odpowiedzi wynika, że ​​getVectorOfFoo() jest funkcją członkowską klasy i że wektor jest zmienną składową klasy, ale nie było to nigdzie w pytaniu, i robi różnicę . –

+0

Dobra dyskusja, nauczyłem się czegoś od wszystkich. Dzięki chłopaki. – Mark

Odpowiedz

9

Pierwszy nie zwróci listę wskaźników.
To sprawia, że ​​jest podwójnie niejasna co do dozwolonych działań.

Boost ma rozwiązanie (jak zwykle).
Zwróć pojemnik wskaźnika. Ujawnia to wskaźniki jako normalne elementy.

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

Teraz użytkownik nie może zmienić zwróconego wektora.

przykład:

#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, Nigdy wcześniej nie myślałem o tym zastosowaniu kontenerów wskaźnika. –

+0

@ Mark B: Ja też. Lubię to. –

1

Zwróć także jako const.

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

Jak już wspomniano, stanowią const dostęp do pojemnika.

Ale nadal nie jest "idealny", ponieważ trzeba odsłonić pojemnik światu, więc jeśli go zmienisz, wymusisz zmianę kodu użytkownika, jeśli interfejs nie będzie już taki sam.

Ale jest ostatnią nadzieją:

Jeśli można użyć lambdy (C++ 0x), to lepiej zapewnić algorytm 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! 

}; 

Zastosowanie:

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

Powiązane: http://en.wikipedia.org/wiki/Visitor_pattern – luke

2

Włączenie typu zwrotu kontenera do podpisu metody raczej uniemożliwi ciągłą zmianę podstawowego typu kontenera w przypadku, gdy alternatywny kontener stanie się bardziej odpowiedni w przyszłości.

Należy przynajmniej rozważyć użycie typedef, aby ukryć rzeczywisty typ kontenera, i udokumentować minimalne możliwości zwracanego obiektu, a nie bezpośrednio zwracać wartość vector.

Można jednak rozważyć udostępnienie interfejsu iteratora, takiego jak YourThing::const_iterator getFooBegin() i getFooEnd(). W ten sposób kod klienta nie może modyfikować podstawowych obiektów OR kontenera, a nawet nie musi nawet wiedzieć, jaki jest typ kontenera, co pozwala na większą elastyczność w przyszłości.

Jeśli wolisz zwrócić pojemnik, musisz dokładnie określić, czym jest semantyka, od pytania, które brzmi, jakby to było tylko do odczytu. W takim przypadku należałoby wykonać kopię kontenera w innym vector, który zawierałby wskaźniki const, a nie stały, więc klient nie mógł ich zmodyfikować. Alternatywnie inna odpowiedź dostarczyła naprawdę dobrej sugestii użycia pojemników wskaźnika doładowania.