2016-02-14 6 views
7

(5.3.4)Hat ein mit Placement neu erstelltes Objekt eine dynamische Speicherdauer?

neuer Ausdruck:

  • :: opt_ neue new-placement_ opt Neuart-id new-initializeropt

  • :: opt_ neue Neu-Platzierung_ opt (Typ-ID) new-initializeropt

Entities erstellt durch eine neue Expression haben dynamische Speicherdauer (3.7.4). [Hinweis: Die Lebensdauer einer solchen Entität ist nicht notwendigerweise beschränkt auf den Bereich, in dem sie erstellt wird. - Endnote]

Ich denke, der Folgendes 1 Hauptobjekt (local_object) mit automatischer Lagerdauer und 3 Blind Klassen mit dynamischer Speicherdauer.

struct dummy 
{ 
    int a; 
}; 

char local_object[256]; 
dummy * a = new(&local_object) dummy; 
dummy * b = new(&local_object +100) dummy; 
dummy * c = new(&local_object +200) dummy; 

Der Benutzer @ M.M. argumentiert, dass es nur ein Objekt (local_object) gibt, und dass der Rest nur Zeiger sind. Ist das richtig?

(3,7)

Die dynamische Speicherdauer mit Objekten zugeordnet ist, erstellt mit Betreibern neuen

+0

'local_object',' a', 'b' und' c' haben automatische Speicherdauer. '* a',' * b' und '* c' nicht. Übrigens sollten Sie bei der Platzierung "neu" auf die Ausrichtung achten. – 5gon12eder

+0

Es ist nur ein zufälliges Beispiel tho. – Jts

+0

Was genau ist deine Frage? Die Zeiger werden baumeln, sobald "lokales_Objekt" den Gültigkeitsbereich verlässt, aber die Destruktoren der platzierten Objekte müssen manuell aufgerufen werden. Oder wolltest du etwas anderes fragen? – 5gon12eder

Antwort

7

Es scheint mir, dass der Standard (wie im OP zitiert) kann nur interpretiert werden, wie es liest, das ist, dass operator new Objekte der dynamischen Speicherdauer erstellt, und dies gilt auch, wenn die un Der Speicher wurde für ein Objekt mit automatischer Dauer erhalten. 3.8 [basic.life] Absatz 8 unter Bezugnahme auf das folgende Beispiel nicht definiertes Verhalten

Dieses genaue Szenario in § von der Norm erwartet:

class T { }; 
struct B { 
    ~B(); 
}; 
void h() { 
    B b; 
    new (&b) T; 
} 

Der Absatz lautet:

Wenn ein Programm die Lebensdauer eines Objekts vom Typ T mit statischer (3.7.1), thread (3.7.2) oder automatischer (3.7.3) Speicherdauer beendet und wenn T einen nicht-trivialen Destruktor hat, muss das Programm sicherstellen dass ein Objekt vom ursprünglichen Typ den gleichen Speicherort belegt, wenn das implizite Objekt Destruktor-Aufruf findet statt; Andernfalls ist das Verhalten des Programms nicht definiert.

Im Beispiel hat das Programm des Objekts „um die Lebensdauer zu Ende“ b durch seine Lagerung Wiederverwendung nach Absatz 4 des gleichen Abschnitts vorgesehen: (Hervorhebung hinzugefügt).

Ein Programm kann die Lebensdauer eines Objekts durch Wiederverwendung des Einlagerseite der das Objekt oder durch expliziten Aufruf der Destruktor für ein Objekt eines Klassentyps mit einer nicht-trivialen destructor einnimmt.

Für ein Objekt einer Klasse-Typ: nicht genannt werden

Im Beispielcode wurde b ‚s destructor nicht genannt, aber das ist akzeptabel, weil ausdrücklich Absatz 4 eine nicht-triviale destructor erlaubt Mit einem nicht-trivialen Destruktor muss das Programm den Destruktor nicht explizit aufrufen, bevor der Speicher, den das Objekt einnimmt, wiederverwendet oder freigegeben wird;

solange das Programm darauf vorbereitet ist, mit den Folgen zu leben, dass der Destruktor nicht aufgerufen wird.

Aber um zu Absatz 8 zurückzukehren, ist die Lebensdauer von b abgelaufen und der Speicher wurde wiederverwendet, um ein Objekt vom Typ T zu erstellen. Dieses Objekt hat eine dynamische Speicherdauer, was bedeutet, dass sein Destruktor nicht implizit aufgerufen wird. Wie oben ist es nicht obligatorisch, den Destruktor explizit aufzurufen, solange das Programm keine Nebenwirkungen benötigt, die vom Destruktor ausgeführt werden könnten.

Trotz der Tatsache, dass die Lebensdauer von b abgelaufen ist, bedeutet die Tatsache, dass b automatische Speicherdauer hatte, dass sein Destruktor implizit aufgerufen wird, wenn der Kontrollfluss seinen Gültigkeitsbereich verlässt. Das Aufrufen eines Destruktors für ein Objekt, dessen Lebensdauer abgelaufen ist, ist ein spezieller Fall des Verbots, ein Objekt zu verwenden, dessen Lebensdauer abgelaufen ist, gemäß Paragraph 6 von § 3.8, das das Aufrufen einer nicht statischen Elementgliedfunktion eines Objekts, dessen Lebensdauer es hat, verbietet beendet, aber dessen Speicher wurde noch nicht wiederverwendet oder freigegeben.

Aus diesem Grund ist das Verhalten des Beispielprogramms undefiniert.

Aber Ziffer 7 dieses Abschnitt stellt einen Mechanismus für das Programm das nicht definiertes Verhalten zu vermeiden, indem ein anderes Objekt des gleichen Typs an derselben Stelle neu zu erstellen:

Wenn nach der Lebensdauer eines Objekts hat beendet und bevor der Speicher, den das Objekt belegt hat, wiederverwendet oder freigegeben wird, wird an dem Speicherplatz, den das ursprüngliche Objekt belegt hat, ein neues Objekt angelegt, ein Zeiger, der auf das ursprüngliche Objekt zeigt, ein Verweis auf das ursprüngliche Objekt oder der Name des ursprünglichen Objekts bezieht sich automatisch auf das neue Objekt und kann, sobald die Lebensdauer des neuen Objekts begonnen hat, verwendet werden, um das neue Objekt zu manipulieren, wenn:

(7.1) - die Lagerung für das neue Objekt genau den Speicherort überlagert, das das ursprüngliche Objekt besetzt, und

(7,2) - das neue Objekt des gleichen Typs wie das ursprüngliche Objekt ist (ohne Berücksichtigung des top- Level cv-Qualifier) ​​und

(7.3) - der Typ des ursprünglichen Objekts ist nicht const-qualifiziert und enthält, wenn ein Klassentyp nicht ein nicht-statisches Datenelement enthält, dessen Typ const-qualifiziert ist, oder einen Referenztyp, und

(7.4) - Das ursprüngliche Objekt war das am meisten abgeleitete Objekt (1.8) vom Typ T und das neue Objekt ist das am meisten abgeleitete Objekt vom Typ T (das heißt, es handelt sich nicht um Unterobjekte der Basisklasse).

Also, meiner Interpretation, das folgende Snippet definiert Verhalten wäre:

class T { }; 
struct B { 
    ~B(); 
}; 
void h() { 
    B b; 
    new (&b) T; 
    new (&b) B; /* recreate a B so that it can be destructed */ 
} 

Kurz gesagt, sieht der Standard die Möglichkeit, dass ein Objekt der dynamischen Speicherdauer kann der Speicher auf einen zugewiesenen erstellt werden Objekt der automatischen Speicherdauer und bietet eine Reihe von Einschränkungen und Anforderungen für ein wohldefiniertes Programm, das diese Aktion ausführt, wodurch die Folgen der Ausführung eines impliziten Destruktors für ein Objekt vermieden werden, dessen Lebensdauer durch Wiederverwendung seines Speichers beendet wurde.

+0

So haben wir 3,8 \ 4: "_der Destruktor darf nicht implizit aufgerufen werden_" - und, 3.8 \ 8: "_wenn der implizite Destruktoraufruf stattfindet_". WTF? –

+0

Oh, es scheint, dass Sie die Lebensdauer von _b_ beenden müssen, indem Sie explizit seinen Destruktor aufrufen, und nicht nur seinen Speicher wiederverwenden; ansonsten gibt es undefiniertes Verhalten von 3.8 \ 4, nicht wahr? –

+0

@eugene: Der implizite Destruktor wird nicht auf dem ursprünglichen Objekt aufgerufen, da sein Speicher erneut verwendet wird.(Logisch, weil es nicht einfach ist zu wissen, dass der Speicher wiederverwendet wird.) Aber der implizite Destruktor wird aufgerufen, also muss an diesem Punkt ein anderes Objekt_ an Ort und Stelle sein. – rici

Verwandte Themen