2010-08-02 5 views
8

Ich stieß auf meinen ersten Compiler, der den an :: delete übergebenen lvalue ändert, aber den lvalue nicht auf Null setzt. Das ist folgendes gilt:Wo im C++ Standard heißt es: :: delete kann lvales ändern?

Foo * p = new Foo(); 
Foo * q = p; 
assert(p != 0); 
assert(p == q); 
::delete p; 
assert(p != q); 
assert(p != 0); 

Beachten Sie, dass p nicht Null nach dem Löschvorgang ist, und es hat sich geändert von ihm alten Wert. Ein Kollege sagte mir, dass dies nicht ungewöhnlich in seiner Erfahrung mit einigen Mainframe C++ - Compiler, die p zu 0xFFFFFFFF ändern würde, sowie andere Compiler, die p zu 0 ändern würde. Wo im C++ Standard sagt es dass ein Compiler dies tun darf?

durch Stackoverflow Suche, fand ich diese Frage: Why doesn’t delete set the pointer to NULL?, die eine Antwort hatte, die Bjarne Stroustrup's response bezeichnet, dass die Aussage enthält:

C++ explizit erlaubt eine Implementierung der Lösch- einen L-Wert-Operanden auf Null, und ich hatte hoffte, dass Implementierungen das tun würden, aber diese Idee scheint bei Implementierern nicht populär geworden zu sein.

Ich habe Abschnitt 5.3.5 und 12.5 der final committee draft C++0x standard gelesen und gelesen, aber ich sehe nicht den "expliziten" Teil. Sehe ich nur in die falschen Bereiche des Standards? Oder gibt es eine Kette von Logik, die in den Abschnitten ist, aber ich bin nur nicht richtig miteinander verbinden.

Ich habe meine Kopie des Annotated C++ Reference Manual nicht mehr. War es in der ARM, dass ein Compiler das tun konnte?

[Bearbeiten: Korrigieren der Abschnittsreferenz von 3.5.3 bis 5.3.5. Ich füge auch ein interessantes Paradox als Kontrapunkt zu Henks Behauptung hinzu, dass p nach dem Löschen undefiniert ist.]

Es gibt ein interessantes Paradox, wenn p auf Null initialisiert wird.

Foo * p = 0; 
Foo * q = p; 
assert(p == 0); 
assert(p == q); 
::delete p; 
assert(p == q); 
assert(p == 0); 

In diesem Fall ist das Verhalten jedoch gut dokumentiert. Wenn delete einen Null-Zeiger erhält, wird angenommen, nichts zu tun, so dass p unverändert bleibt.

+0

Nun, der ARM ist weder hier noch dort. Und lasst uns lvales vergessen. Ist Ihre Frage "Kann löschen sein Argument ändern?". –

+0

Der Standard sagt auch nicht, was ein Compiler tun darf - er gibt nur an, was er tun darf und was nicht. Ich kann in der aktuellen Norm nichts sehen, was verbietet, dass der Zeigerwert geändert wird. –

+1

Ich habe meine Frage nicht als "Kann löschen ändern sein Argument?" weil ich nicht darüber nachdenken wollte, wie eine Klasse, die den Operator deklarieren will, die Änderung ihres Arguments ändert, da sie nur void *, nicht void * & oder void ** akzeptiert. – Ants

Antwort

8

Es könnte nicht so explizit sein. In 5.3.5/7 heißt es, dass der Ausdruck delete eine Deallocator-Funktion aufruft. Dann heißt es in 3.7.3.2/4, dass die Verwendung eines nicht zugeordneten Zeigers undefiniert ist. Da der Wert des Zeigers nach der Aufhebung der Freigabe nicht verwendet werden kann, macht es keinen Unterschied, ob der Zeiger den Wert beibehält oder der Wert durch die Implementierung geändert wird.

5.3.5/7

Der Lösch-Ausdruck wird eine Aufhebung der Zuordnung Funktion (3.7.3.2) nennen.

3.7.3.2/4

Wenn das Argument zu einer Aufhebung der Zuordnung Funktion in der Standardbibliothek Gegeben ist ein Zeiger, der nicht der Null-Zeiger-Wert (4,10), die Aufhebung der Zuordnung des Speicherfunktion wird freigeben ist referenziert durch den Zeiger, wobei alle Zeiger, die sich auf irgendeinen Teil des freigegebenen Speichers beziehen, ungültig gemacht werden. Der Effekt der Verwendung eines ungültigen Zeigerwerts (einschließlich der Übergabe an eine Zuordnungsfunktion) ist nicht definiert.

Die Referenzen stammen aus dem aktuellen Standard. In dem kommenden Standard 5.3.5/7 hat umformuliert:

C++ 0x FD 5.3.5/7

Wenn der Wert des Operanden der Lösch-expression keinen Null-Zeiger-Wert , ruft der delete-expression eine Deallocation-Funktion auf (3.7.4.2). Ansonsten ist nicht angegeben, ob die Freigabe-Funktion aufgerufen wird. [Hinweis: Die Funktion zum Aufheben der Zuweisung wird unabhängig davon aufgerufen, ob der Destruktor für das Objekt oder ein Element des Arrays eine Ausnahme auslöst. - Endnote]

+0

OK, ich nehme dies als Unterstützung für meinen Standpunkt. Wenn die Verwendung eines ungültigen Zeigers undefiniert ist, kann ein intelligenter Compiler Zeiger, von denen er weiß, dass sie ungültig sind, ändern, so dass Fehler besser angezeigt werden. Fail schnell, scheitern früh. –

+2

@Henk: Ich stimme der Absicht des Compilers zu, die Philosophie "Fail Fast, Fail Early" zu unterstützen. Es wurde ein Fehler im Code entdeckt, der nun seit fast 10 Jahren ausgeliefert wird. Wir haben eine doppelt verknüpfte Listenimplementierung, die den nächsten, vorherigen und ersten Knotenzeiger im Destruktor eines Knotens automatisch aktualisiert. Der Destruktor der Liste wurde als "while (m_firstNode) delete m_firstNode;" Der Knoten-Destruktor aktualisiert m_firstNode korrekt, aber der Compiler überschreibt m_firstNode nach dem Destruktoraufruf mit einem Wert ungleich null. Kaboom! Wenn es explizit wäre, wäre der ursprüngliche Code anders gewesen. – Ants

+0

@Anzeigen: interessant. Dies bringt es an den Rand und fügt der Mischung Aliasing hinzu. –