2016-11-01 32 views
5

mam nieco zmieszany ostatnio o pamięć (de) alokacji std::vectorsPytania dotyczące memorybehavior wektorów

Załóżmy mam normalny wektor liczb całkowitych: std::vector<int> intv; Kiedy push_back kilka int „s rośnie przez czas. A kiedy opuszczę zakres (tj.) Funkcji, zostanie ona zwolniona bez potrzeby dodatkowych wywołań.

Świetnie. Załóżmy inny przykład:

struct foo_t{ 
    std::string bar: 
    unsigned int derp; 
} 
void hurr(){ 
    std::vector<foo_t> foov; 
    foo_t foo; 
    foo.bar = "Sup?"; 
    foo.derp = 1337; 
    foov.push_back(foo); 
} 

OK. Kiedy zadzwonię pod numer hurr(), wektor zostanie utworzony, instancja foo_t zostanie utworzona, instancja zostanie wypełniona i wypchnięta do wektora. Więc kiedy opuszczę tę funkcję, wektor zostaje zwolniony, a zawartość (tutaj jedna foo_t) zostaje również zwolniona.

Następny przykład:

struct foo_t{ 
    std::string bar: 
    unsigned int derp; 
} 
std::vector<foo_t> hurr(){ 
    std::vector<foo_t> foov; 
    foo_t foo; 
    foo.bar = "Sup?"; 
    foo.derp = 1337; 
    foov.push_back(foo); 
    return foov; 
} 

W moim rozumieniu, wektor i jego zawartość żyć w stosie, która może (ewentualnie) nadpisane przez czas i wektor wróciłem i jego zawartość będzie bezużyteczny. Czy rzeczywiście zwraca kopię wektora z kopią jego zawartości (wymaga Konstruktora kopii dla typu danych zawartości, jeśli nie jest to POD)?

I coś oczywistego:

struct foo_t{ 
    std::string bar: 
    unsigned int derp; 
} 
std::vector<foo_t*> hurr(){ 
    std::vector<foo_t*> foov; 
    foo_t foo = new foo_t; 
    foo->bar = "Sup?"; 
    foo->derp = 1337; 
    foov.push_back(foo); 
    return foov; 
} 

Teraz muszę ręcznie iteracyjne nad wektorem, usuń jego zawartość, a następnie można bezpiecznie pozwolić wektor wypaść z zakresu, prawda?

+0

_ "Teraz muszę ręcznie wykonać iteracje po wektorze, usunąć jego zawartość, a następnie mogę bezpiecznie pozwolić, by wektor wypadł z zakresu, prawda?" _ Tak. –

+1

"Teraz muszę ręcznie wykonać iterację wektora, usunąć jego zawartość, a następnie mogę bezpiecznie pozwolić, by wektor wypadł z zakresu, prawda?" To unieważniłoby wskaźniki w zwróconym wektorze. Chyba że tego chcesz i prawdopodobnie tak nie jest, nie powinieneś. – molbdnilo

+0

@molbdnilo Zależy, kiedy _Now_ ma się wydarzyć. –

Odpowiedz

3

Przykład:

struct foo_t{ 
    std::string bar; 
    unsigned int derp; 
}; 
void hurr(){ 
    std::vector<foo_t> foov; 
    foo_t foo; 
    foo.bar = "Sup?"; 
    foo.derp = 1337; 
    foov.push_back(foo); 
} 

hurv() Po zakończeniu foov i foo oba zwolniona.

std::vector<foo_t> hurr(){ 
    std::vector<foo_t> foov; 
    foo_t foo; 
    foo.bar = "Sup?"; 
    foo.derp = 1337; 
    foov.push_back(foo); 
    return foov; 
} 

wynikiem std::vector<foo_t> od hurr() jest ważna z 1 foo_t w nim i to jest ważne. return foov;może nazwać kopiowaniem contructor z std::vector<foo_t>, i mieć swobodę nie zrobić tej kopii, patrz copy elision

W każdym razie, od C++ 11, można napisać to:

struct foo_t{ 
    std::string bar; 
    unsigned int derp; 
    // we will copy the string anyway, pass-by-value 
    foo_t(std::string bar_, unsigned int d_) 
     : bar(std::move(bar_)), derp(d_) {} 
}; 
std::vector<foo_t> hurr(){ 
    std::vector<foo_t> foov; 
    // This is better, in place construction, no temporary 
    foov.emplace_back("Sup?", 1337); 
    // This require a temporary 
    foov.push_back(foo_t{"Sup?", 1337}); 
    return foov; 
} 

W ostatnim przykładzie tak, musisz ręcznie wykonać iterację po wektorze, usunąć jego zawartość, a następnie bezpiecznie pozwolić, by wektor wypadł z zakresu, gdy nie chcesz już używać wyniku hurr() (nie w hurr())

+0

przegapiłeś punkt 'emplace_back'. Chodzi o to, aby budować na miejscu, unikając potrzeby tymczasowego. Powinieneś zrobić 'emplace_back (" sup ", 1337)' – bolov

+0

@bolov faktycznie, nie można go skompilować z 'emplace_back (" sup ", 1337)', 'emplace_back' potrzebuje konstruktora, prawda? – Danh

+0

tak, ale 'emplace_back' będzie w tym przypadku takie samo, jak' push_back', tzn. Przeniesienie tymczasowego do wektora. – Asu

2

Więc kiedy opuszczę funkcję, wektor zostaje zwolniony, a zawartość (tutaj jedna foo_t) zostaje również zwolniona.

Tak. A jeśli foo_t miałby nietrywialny destruktor, byłby wywoływany.

Albo robi to faktycznie zwraca kopię wektora z kopią jego treści (wymaga kopiowaniem konstruktor typów danych zawartości jeśli jego a nie POD)?

Tak, w takim przypadku zwraca kopię. Nowoczesne kompilatory prawdopodobnie wywołają konstruktor kopiowania dla std::vector, który z kolei wywoła konstruktor kopiowania zawartego typu dla każdego elementu. C++ 17 wprowadza gwarantowaną optymalizację wartości zwracanej (RVO), więc konstruktor kopii twojego wektora nie zostanie wywołany. Niemniej jednak, jeśli ustawisz wysoki poziom optymalizacji, nowoczesny kompilator może również używać RVO.

Teraz muszę ręcznie iteracyjne nad wektorem, usuń jego zawartość i wtedy mogę bezpiecznie niech wektor spadek poza zakresem, prawda?

Tak, masz rację. Rozważ użycie inteligentnych wskaźników, jeśli nie chcesz ręcznie wykonywać iteracji.

+0

W moim [programie testowym] (http://coliru.stacked-crooked.com/a/3442c38380fbe573), g ++ w '-O0' * faktycznie * wydaje się korzystać z RVO. * edit: * właściwie nie skompilowałem w '-O0' w tym programie, ale jeśli go określisz, uzyskasz takie same wyniki. – Asu

+0

Używanie smartpointów było kolejnym przykładem dla mojego pytania, które pominąłem. –

+0

Nawet jeśli nie ma opcji kopiowania, użyty zostanie konstruktor ruchu (jeśli zawarty typ nie jest ruchomy) –

3
foov.push_back(foo); 

Właściwie skonstruowane foo_v i pchnął go z powrotem, który faktycznie stworzył nową foo_v i nazwał konstruktor kopiujący z foov jako paremeter. Użyj emplace_back, jeśli chcesz tego uniknąć.

return foov; 

Kompilator może zoptymalizować to za pomocą optymalizacji wartości zwracanej. Zobacz this short program Jako przykład podałem działanie na coliru. Zobacz inne doskonałe odpowiedzi w this question.

std::vector<foo_t*> foov; 
/* add elements to foov with new */ 

Teraz muszę ręcznie itterate nad wektorem, usuń jego zawartość, a następnie można bezpiecznie pozwolić upadek wektor poza zakresem, prawda?

Tak, robisz. Z tych samych powodów

int* a = new int(); 

nie delete a; gdy już zostaną a umiera.