To jest podstawowa zasada C ++. Obiekty są wartościami. Cesja tworzy kopię. Dwie zmienne odwołujące się do tego samego obiektu nie są możliwe, chyba że zmodyfikujesz typ za pomocą *lub, &aby utworzyć wskaźnik lub odniesienie.
Daniel Earwicker,
8
@DanielEarwicker push_back faktycznie pobiera odniesienie. Z samego podpisu nie wynika jasno, że utworzy kopię.
Brian Gordon,
3
@BrianGordon - Nie mówię, że tak jest! Stąd potrzeba przewodniej zasady. Mimo to możemy wywnioskować coś z podpisu push_back: zajmuje const&. Albo odrzuca wartość (bezużyteczna), albo istnieje metoda pobierania. Patrzymy więc na podpis backi zwraca on zwykły &, więc albo oryginalna wartość została skopiowana, albo constzostała po cichu odrzucona (bardzo źle: potencjalnie niezdefiniowane zachowanie). Zakładając więc, że projektanci vectorbyli racjonalni ( vector<bool>nie sprzeciwiali się), dochodzimy do wniosku, że robi kopie.
Daniel Earwicker,
Odpowiedzi:
183
Tak, std::vector<T>::push_back()tworzy kopię argumentu i przechowuje ją w wektorze. Jeśli chcesz przechowywać wskaźniki do obiektów w swoim wektorze, utwórz plik std::vector<whatever*>zamiast std::vector<whatever>.
Musisz jednak upewnić się, że obiekty, do których odwołują się wskaźniki, pozostają prawidłowe, podczas gdy wektor zawiera odniesienie do nich (inteligentne wskaźniki wykorzystujące idiom RAII rozwiązują problem).
Chciałbym również zauważyć, że jeśli używasz surowych wskaźników, jesteś teraz odpowiedzialny za ich czyszczenie. Nie ma dobrego powodu, aby to robić (zresztą nie taki, który mi przychodzi do głowy), zawsze powinieneś używać inteligentnego wskaźnika.
Od C ++ 11 push_backwykona ruch zamiast kopii, jeśli argument jest odwołaniem do wartości r. (Obiekty można konwertować na referencje rvalue za pomocą std::move().)
emlai
2
@tuple_cat Twój komentarz powinien brzmieć „jeśli argument jest wartością r”. (Jeśli argumentem jest nazwa jednostki zadeklarowanej jako odniesienie do wartości r, to argument jest w rzeczywistości lwartością i nie zostanie przeniesiony z) - sprawdź moją edycję odpowiedzi „Karl Nicoll”, który popełnił ten błąd na początku
MM
Poniżej jest odpowiedź, ale dla jasności: od C ++ 11 używaj również, emplace_backaby uniknąć kopiowania lub przenoszenia (konstruuj obiekt w miejscu dostarczonym przez kontener).
Wormer,
34
Tak, std::vectorprzechowuje kopie. Skąd powinieneś vectorwiedzieć, jakie są przewidywane czasy życia twoich obiektów?
Jeśli chcesz przenieść lub udostępnić własność obiektów, użyj wskaźników, być może inteligentnych wskaźników, takich jak shared_ptr(znalezione w Boost lub TR1 ), aby ułatwić zarządzanie zasobami.
naucz się używać shared_ptr - robią dokładnie to, co chcesz. Moim ulubionym idiomem jest typedef boost :: shared_ptr <Foo> FooPtr; Następnie stwórz kontenery FooPtrs
pm100
3
@ pm100 - Czy wiesz boost::ptr_vector?
Manuel
2
Lubię też class Foo { typedef boost::shared_ptr<Foo> ptr; };po prostu pisać Foo::ptr.
shared_ptr jest dobre, jeśli masz współwłasność, ale jest ogólnie nadużywane. unique_ptr lub boost scoped_ptr mają znacznie większy sens, gdy własność jest jasna.
Nemanja Trifunovic
28
Od c ++ 11 roku, wszystkie standardowe pojemniki ( std::vector, std::mapitp) semantyka wsparcie poruszać, co oznacza, że można teraz przejść rvalues do standardowych pojemników i uniknąć kopię:
// Example object class.classobject{private:int m_val1;
std::string m_val2;public:// Constructor for object class.object(int val1, std::string&&val2):
m_val1(val1),
m_val2(std::move(val2)){}};
std::vector<object> myList;// #1 Copy into the vector.object foo1(1,"foo");
myList.push_back(foo1);// #2 Move into the vector (no copy).object foo2(1024,"bar");
myList.push_back(std::move(foo2));// #3 Move temporary into vector (no copy).
myList.push_back(object(453,"baz"));// #4 Create instance of object directly inside the vector (no copy, no move).
myList.emplace_back(453,"qux");
Alternatywnie możesz użyć różnych inteligentnych wskaźników, aby uzyskać w większości ten sam efekt:
std::unique_ptr przykład
std::vector<std::unique_ptr<object>> myPtrList;// #5a unique_ptr can only ever be moved.auto pFoo = std::make_unique<object>(1,"foo");
myPtrList.push_back(std::move(pFoo));// #5b unique_ptr can only ever be moved.
myPtrList.push_back(std::make_unique<object>(1,"foo"));
std::shared_ptr przykład
std::vector<std::shared_ptr<object>> objectPtrList2;// #6 shared_ptr can be used to retain a copy of the pointer and update both the vector// value and the local copy simultaneously.auto pFooShared = std::make_shared<object>(1,"foo");
objectPtrList2.push_back(pFooShared);// Pointer to object stored in the vector, but pFooShared is still valid.
Zauważ, że std::make_uniquejest (irytująco) dostępny tylko w C ++ 14 i nowszych. Upewnij się, że powiesz kompilatorowi, aby odpowiednio ustawił zgodność ze standardem, jeśli chcesz skompilować te przykłady.
Laryx Decidua
W 5a możesz użyć, auto pFoo =aby uniknąć powtórzeń; i wszystkie std::stringrzutowania można usunąć (istnieje niejawna konwersja z literałów łańcuchowych do std::string)
MM
2
@ user465139 make_uniquemożna łatwo zaimplementować w C ++ 11, więc jest to tylko drobna irytacja dla kogoś, kto utknął w kompilatorze C ++ 11
@Anakin - Tak, powinni to zrobić, ale tylko wtedy, gdy kopiujesz. Jeśli używasz std::move()with std::shared_ptr, oryginalny wskaźnik współdzielony może mieć zmieniony wskaźnik, ponieważ własność została przekazana do wektora. Zobacz tutaj: coliru.stacked-crooked.com/a/99d4f04f05e5c7f3
Karl Nicoll,
15
std :: vector zawsze tworzy kopię tego, co jest przechowywane w wektorze.
Jeśli zachowujesz wektor wskaźników, utworzy on kopię wskaźnika, ale nie instancji, na którą wskazuje wskaźnik. Jeśli masz do czynienia z dużymi obiektami, możesz (i prawdopodobnie powinieneś) zawsze używać wektora wskaźników. Często użycie wektora inteligentnych wskaźników odpowiedniego typu jest dobre ze względów bezpieczeństwa, ponieważ w przeciwnym razie obsługa czasu życia obiektu i zarządzanie pamięcią może być trudne.
nie zależy od typu. Zawsze tworzy kopię. Jeśli jego wskaźnik jest kopią wskaźnika
pm100
Oboje macie rację. Technicznie tak, zawsze tworzy kopię. Praktycznie, jeśli przekażesz mu wskaźnik do obiektu, skopiuje on wskaźnik, a nie obiekt. Powinieneś bezpiecznie używać odpowiedniego inteligentnego wskaźnika.
Steven Sudit
1
Tak, to zawsze kopiuje - jednak „obiekt”, do którego odnosi się OP, jest najprawdopodobniej klasą lub strukturą, więc odnosiłem się do tego, czy kopiowanie „obiektu” zależy od definicji. Jednak źle sformułowane.
Reed Copsey
3
Nie tylko std :: vector tworzy kopię tego, co odpychasz, ale definicja zbioru mówi, że to zrobi i że nie możesz używać obiektów bez poprawnej semantyki kopiowania w wektorze. Na przykład nie używasz auto_ptr w wektorze.
W C ++ 11 istotna jest emplacerodzina funkcji składowych, które umożliwiają przenoszenie własności obiektów poprzez przenoszenie ich do kontenerów.
Tak wyglądałby idiom użycia
std::vector<Object> objs;Object l_value_obj {/* initialize */};// use object here...
objs.emplace_back(std::move(l_value_obj));
Przeniesienie obiektu lvalue jest ważne, ponieważ w przeciwnym razie zostałby przekazany jako referencja lub odwołanie do stałej, a konstruktor przenoszenia nie zostałby wywołany.
jeśli nie chcesz kopii; wtedy najlepszym sposobem jest użycie wektora wskaźnikowego (lub innej struktury, która służy temu samemu celowi). jeśli chcesz kopie; użyj bezpośrednio push_back (). nie masz innego wyboru.
Uwaga dotycząca wektorów wskaźnikowych: vector <shared_ptr <obj>> jest dużo bezpieczniejszy niż vector <obj *>, a shared_ptr jest częścią standardu z zeszłego roku.
bogaty. E
-1
Dlaczego zajęło nam to dużo badań w Valgrind, żeby się tego dowiedzieć! Po prostu udowodnij to sobie prostym kodem, np
To nie stanowi dowodu. Gdyby obiekt nie został skopiowany, ostatnia instrukcja byłaby niezdefiniowanym zachowaniem i mogłaby wydrukować hello.
Mat
4
poprawnym testem byłaby modyfikacja jednego z dwóch po wstawieniu. Gdyby były tym samym obiektem (gdyby wektor przechowywał odniesienie), oba zostałyby zmodyfikowane.
*
lub,&
aby utworzyć wskaźnik lub odniesienie.push_back
: zajmujeconst&
. Albo odrzuca wartość (bezużyteczna), albo istnieje metoda pobierania. Patrzymy więc na podpisback
i zwraca on zwykły&
, więc albo oryginalna wartość została skopiowana, alboconst
została po cichu odrzucona (bardzo źle: potencjalnie niezdefiniowane zachowanie). Zakładając więc, że projektancivector
byli racjonalni (vector<bool>
nie sprzeciwiali się), dochodzimy do wniosku, że robi kopie.Odpowiedzi:
Tak,
std::vector<T>::push_back()
tworzy kopię argumentu i przechowuje ją w wektorze. Jeśli chcesz przechowywać wskaźniki do obiektów w swoim wektorze, utwórz plikstd::vector<whatever*>
zamiaststd::vector<whatever>
.Musisz jednak upewnić się, że obiekty, do których odwołują się wskaźniki, pozostają prawidłowe, podczas gdy wektor zawiera odniesienie do nich (inteligentne wskaźniki wykorzystujące idiom RAII rozwiązują problem).
źródło
push_back
wykona ruch zamiast kopii, jeśli argument jest odwołaniem do wartości r. (Obiekty można konwertować na referencje rvalue za pomocąstd::move()
.)emplace_back
aby uniknąć kopiowania lub przenoszenia (konstruuj obiekt w miejscu dostarczonym przez kontener).Tak,
std::vector
przechowuje kopie. Skąd powinieneśvector
wiedzieć, jakie są przewidywane czasy życia twoich obiektów?Jeśli chcesz przenieść lub udostępnić własność obiektów, użyj wskaźników, być może inteligentnych wskaźników, takich jak
shared_ptr
(znalezione w Boost lub TR1 ), aby ułatwić zarządzanie zasobami.źródło
boost::ptr_vector
?class Foo { typedef boost::shared_ptr<Foo> ptr; };
po prostu pisaćFoo::ptr
.shared_ptr
nie jest dokładnie ogień i zapomnij. Zobacz stackoverflow.com/questions/327573 i stackoverflow.com/questions/701456Od c ++ 11 roku, wszystkie standardowe pojemniki (
std::vector
,std::map
itp) semantyka wsparcie poruszać, co oznacza, że można teraz przejść rvalues do standardowych pojemników i uniknąć kopię:Alternatywnie możesz użyć różnych inteligentnych wskaźników, aby uzyskać w większości ten sam efekt:
std::unique_ptr
przykładstd::shared_ptr
przykładźródło
std::make_unique
jest (irytująco) dostępny tylko w C ++ 14 i nowszych. Upewnij się, że powiesz kompilatorowi, aby odpowiednio ustawił zgodność ze standardem, jeśli chcesz skompilować te przykłady.auto pFoo =
aby uniknąć powtórzeń; i wszystkiestd::string
rzutowania można usunąć (istnieje niejawna konwersja z literałów łańcuchowych dostd::string
)make_unique
można łatwo zaimplementować w C ++ 11, więc jest to tylko drobna irytacja dla kogoś, kto utknął w kompilatorze C ++ 11template<typename T, typename... Args> unique_ptr<T> make_unique(Args&&... args) { return unique_ptr<T>{new T{args...}}; }
std::move()
withstd::shared_ptr
, oryginalny wskaźnik współdzielony może mieć zmieniony wskaźnik, ponieważ własność została przekazana do wektora. Zobacz tutaj: coliru.stacked-crooked.com/a/99d4f04f05e5c7f3std :: vector zawsze tworzy kopię tego, co jest przechowywane w wektorze.
Jeśli zachowujesz wektor wskaźników, utworzy on kopię wskaźnika, ale nie instancji, na którą wskazuje wskaźnik. Jeśli masz do czynienia z dużymi obiektami, możesz (i prawdopodobnie powinieneś) zawsze używać wektora wskaźników. Często użycie wektora inteligentnych wskaźników odpowiedniego typu jest dobre ze względów bezpieczeństwa, ponieważ w przeciwnym razie obsługa czasu życia obiektu i zarządzanie pamięcią może być trudne.
źródło
Nie tylko std :: vector tworzy kopię tego, co odpychasz, ale definicja zbioru mówi, że to zrobi i że nie możesz używać obiektów bez poprawnej semantyki kopiowania w wektorze. Na przykład nie używasz auto_ptr w wektorze.
źródło
W C ++ 11 istotna jest
emplace
rodzina funkcji składowych, które umożliwiają przenoszenie własności obiektów poprzez przenoszenie ich do kontenerów.Tak wyglądałby idiom użycia
Przeniesienie obiektu lvalue jest ważne, ponieważ w przeciwnym razie zostałby przekazany jako referencja lub odwołanie do stałej, a konstruktor przenoszenia nie zostałby wywołany.
źródło
jeśli nie chcesz kopii; wtedy najlepszym sposobem jest użycie wektora wskaźnikowego (lub innej struktury, która służy temu samemu celowi). jeśli chcesz kopie; użyj bezpośrednio push_back (). nie masz innego wyboru.
źródło
Dlaczego zajęło nam to dużo badań w Valgrind, żeby się tego dowiedzieć! Po prostu udowodnij to sobie prostym kodem, np
Jeśli drukowany jest tekst „hello world”, obiekt musi zostać skopiowany
źródło