2016-11-01 3 views
5

Ich habe ein bisschen verwirrt in letzter Zeit über den Speicher (de) Zuweisung von std::vectorsFragen zu memorybehavior von Vektoren

Nehmen wir an, ich Normalvektor integer bekam: std::vector<int> intv; Wenn ich einige int ‚s push_back wächst es von Zeit. Und wenn ich den Gültigkeitsbereich (d. H.) Der Funktion verlasse, wird die Zuordnung aufgehoben, ohne dass zusätzliche Aufrufe erforderlich sind.

Großartig. Lässt ein weiteres Beispiel haben:

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); 
} 

Okay. Wenn ich hurr() rufe, wird der Vektor erstellt, eine foo_t Instanz wird erstellt, die Instanz wird gefüllt und in den Vektor geschoben. Also, wenn ich die Funktion verlasse, wird der Vektor freigegeben und der Inhalt (hier ein foo_t) wird ebenfalls freigegeben?

Nächstes Beispiel:

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; 
} 

In meinem Verständnis der Vektor und dessen Inhalt in dem Stapel leben, die (eventuell) wird von Zeit überschrieben und der Vektor ich zurückgekehrt und sein Inhalt wird nutzlos sein. Oder gibt es tatsächlich eine Kopie des Vektors mit einer Kopie seines Inhalts zurück (erfordert einen Kopierkonstruktor für den Inhaltsdatentyp, wenn es kein POD ist)?

Und etwas offensichtlich:

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; 
} 

Jetzt muss ich manuell über den Vektor laufen, dessen Inhalt löschen und dann kann ich sicher den Vektor fallen out of scope lassen, nicht wahr?

+0

_ "Jetzt muss ich manuell über den Vektor iterieren, lösche seinen Inhalt und dann kann ich den Vektor sicher außerhalb des Bereichs fallen lassen, richtig?" _ Ja. –

+1

"Jetzt muss ich manuell über den Vektor iterieren, seinen Inhalt löschen und dann kann ich den Vektor sicher außerhalb des Bereichs fallen lassen, richtig?" Das würde die Zeiger in dem zurückgegebenen Vektor ungültig machen. Wenn Sie das nicht wollen und es wahrscheinlich nicht ist, sollten Sie es nicht tun. – molbdnilo

+0

@molbdnilo Hängt davon ab, wann _Now_ passieren soll. –

Antwort

3

Dieses Beispiel:

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); 
} 

Nach hurv() abgeschlossen, foov und foo beide befreit werden.

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; 
} 

das Ergebnis std::vector<foo_t> von hurr() ist mit 1 foo_t darin gültig und es ist gültig. Die return foov;kann rufen eine Kopie contructor von std::vector<foo_t>, und es hat seine freien, nicht die Kopie zu machen, siehe copy elision

Wie auch immer, von C++ 11, können Sie schreiben:

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; 
} 

Und für das letzte Beispiel, ja, müssen Sie manuell über den Vektor zu iterieren, löschen Sie den Inhalt und dann kann ich sicher den Vektor aus dem Geltungsbereich fallen lassen, wenn Sie nicht mehr das Ergebnis hurr() verwenden möchten, (nicht in hurr())

+0

Sie haben den Punkt von "emplace_back" verpasst. Es geht darum, an Ort und Stelle zu konstruieren, die Notwendigkeit einer vorübergehenden zu vermeiden. Sie sollten 'emplace_back (" sup ", 1337)' – bolov

+0

@bolov tatsächlich, es kann nicht kompiliert werden mit 'emplace_back (" sup ", 1337)', 'emplace_back' brauchen einen Konstruktor, oder? – Danh

+0

ja tut es, aber 'emplace_back' wird in diesem Fall dasselbe tun wie 'push_back', d. H. Eine temporäre in den Vektor verschieben. – Asu

2

Also, wenn ich die Funktion verlassen, wird der Vektor freigegeben und der Inhalt (hier ein foo_t) wird auch freigegeben?

Ja. Und wenn foo_t nichttrivialen Destruktor hatte, würde es aufgerufen werden.

Oder ist es tatsächlich eine Kopie des Vektors mit einer Kopie seines Inhalt zurückgibt (erfordert einen Copy-Konstruktor für den Inhalt Datentyp, wenn seine kein POD)?

Ja, in diesem Fall gibt es eine Kopie zurück. Moderne Compiler rufen wahrscheinlich den Kopierkonstruktor für std::vector auf, der wiederum den Kopierkonstruktor des enthaltenen Typs für jedes Element aufruft. C++ 17 führt eine garantierte Rückgabewertoptimierung (RVO) ein, so dass der Kopierkonstruktor Ihres Vektors nicht aufgerufen wird. Wenn Sie jedoch eine hohe Optimierungsstufe einstellen, kann der moderne Compiler auch RVO verwenden.

Jetzt muss ich manuell über den Vektor laufen, löschen seinen Inhalt und dann kann ich sicher der Vektor fallen aus Rahmen, rechts lassen?

Ja, Sie haben Recht. Ziehen Sie Smartpointer in Betracht, wenn Sie nicht manuell iterieren möchten.

+0

In meinem [Testprogramm] (http://coliru.stacked-crooked.com/a/3442c38380fbe573) scheint g ++ in '-O0' * tatsächlich * RVO zu verwenden. * edit: * Ich habe in diesem Programm nicht in '-O0' kompiliert, aber wenn Sie es angeben, erhalten Sie die gleichen Ergebnisse. – Asu

+0

Die Verwendung von Smartpointern war ein weiteres Beispiel für meine Frage, die ich ausgelassen habe. –

+0

Auch wenn keine Kopie vorhanden ist, wird der move-constructor verwendet (wenn der enthaltene Typ nicht "throw-movable" ist). –

3
foov.push_back(foo); 

Eigentlich konstruierte man ein foo_v und Sie schob ihn zurück, das ist eigentlich ein neues foo_v erstellt und rief den Copykonstruktor mit foov als paremeter. Verwenden Sie emplace_back, wenn Sie dies vermeiden möchten.

return foov; 

Der Compiler kann dies optimieren, indem er die Rückgabewertoptimierung verwendet. Siehe this short program Ich habe das Laufen auf Coliru als ein Beispiel gemacht. Beziehen Sie sich auf die anderen ausgezeichneten Antworten in this question.

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

Jetzt muss ich manuell itterate über den Vektor, dessen Inhalt löschen und dann kann ich sicher den Vektor fallen out of scope lassen, nicht wahr?

Ja, das tust du. Aus den gleichen Gründen

int* a = new int(); 

Will nicht delete a; wenn a stirbt.