2010-06-22 13 views
14

Wenn ich C++ Referenzen richtig interpretiere, sind sie wie Zeiger, aber mit garantierter Datenintegrität (keine NULL, no (int *) 0x12345). Was aber passiert, wenn der Bereich des referenzierten Objekts verlassen wird? Wenn keine Magie involviert ist (und wahrscheinlich auch nicht), wird das referenzierte Objekt hinter meinem Rücken zerstört.Was passiert, wenn die C++ - Referenz ihren Gültigkeitsbereich verlässt?

schrieb ich ein Stück Code, dies zu überprüfen:

#include <iostream> 
using namespace std; 

class A { 
public: 
     A(int k) { _k = k; }; 
     int get() { return _k; }; 
     int _k; 
}; 

class B { 
public: 
     B(A& a) : _a(a) {} 
     void b() { cout << _a.get(); } 
     A& _a; 
}; 

B* f() { 
     A a(10); 
     return new B(a); 
} 

int main() { 
     f()->b(); 
} 

Das _k Instanz-Variable wurde gesetzt in Stapelrahmen Existenz zu überprüfen.

Es ist nicht überraschend segfault und ‚10‘ statt korrekt gedruckt werden, während ich nehme an, dass A auf Stapel zugeordnet ist, und dass Stapelrahmen von f() wird von mindestens cout<< Aufruf überschrieben werden.

+5

C++ hat keine "Links", vielleicht meinen Sie "Referenzen"? –

+3

* "Während ich annehme, dass A auf Stack zugewiesen ist und dieser Stack-Frame von f() mindestens überschrieben wird mit call" call "* - scheinst du zu erkennen, dass dies zu undefiniertem Verhalten führt, also warum überhaupt fragen? –

+0

@Evan: sicher, nur das translatorische Problem. – whitequark

Antwort

22

Dies ist undefiniertes Verhalten und Sie waren einfach glücklich, dass der Speicher für a noch für nichts anderes verwendet wurde. In einem komplexeren Szenario würden Sie fast sicher Müll bekommen. Auf meinem Rechner bekomme ich mit diesem Code einen zufälligen Müll. Für mich ist dies wahrscheinlich, weil ich eine 64-Bit-Maschine verwende, die eine Register-Aufrufkonvention verwendet. Register werden viel häufiger als Hauptspeicher (idealerweise ...) wiederverwendet.

So, um Ihre Frage zu beantworten "was passiert". Nun, in diesem Szenario ist die Referenz wahrscheinlich wenig mehr als ein begrenzter Zeiger mit freundlicherer Syntax :-). Unter der Haube ist die Adresse a gespeichert. Später geht das Objekt a nicht mehr in den Geltungsbereich, aber die Referenz des Objekts B auf dieses a wird nicht "automatisch magisch" aktualisiert, um dies widerzuspiegeln. Daher haben Sie jetzt eine ungültige Referenz.

Die Verwendung dieser ungültigen Referenz liefert fast alles, manchmal Abstürze, manchmal nur Koje-Daten.


EDIT: Dank omnifarious, ich habe darüber nachgedacht. Es gibt eine Regel in C++, die grundsätzlich besagt, dass, wenn Sie eine Const-Referenz auf ein Temporäres haben, die Lebensdauer des Temporären mindestens so lang ist wie die Const-Referenz. Was führte zu einer neuen Frage.

EDIT: für die Interessenten an andere Frage Verschoben (const reference to temporary oddity)

+0

War fast sicher, aber fragte nur, ob C++ irgendwo einen versteckten Referenzzähler hat;) Danke trotzdem. – whitequark

+0

Um das klar zu sagen: C++ hat keine eingebaute Referenzzählung ** irgendwo **. Sie bekommen genau das, wonach Sie fragen, und nicht mehr. Es gibt jedoch Bibliotheken, die eine Referenzzählung implementieren (wie zum Beispiel "boost :: shared_ptr" oder sogar "std :: tr1 :: shared_ptr"). –

+3

@whitequark: Die Compiler-Art führt in einigen wenigen Fällen eine Kompilier-Referenzzählung aus. Wenn Sie etwas wie "A & x = A (10);" tun, ist garantiert, dass das 'A' Objekt, auf das' x' verweist, nicht zerstört wird, bis 'x' den Gültigkeitsbereich verlässt. Das funktioniert nur, wenn 'x' den Blockbereich hat. Es wird nicht funktionieren, wenn 'x' eine Membervariable ist oder (denke ich) eine globale oder statische Variable. – Omnifarious

6

In C++ Sprache die Lebensdauer des Objekts die Referenz gebunden ist an die Lebensdauer der Referenz selbst in keiner Weise gebunden, mit nur eine Ausnahme (siehe unten).

Wenn das Objekt vor der Referenz zerstört wird, wird die Referenz ungültig (wie ein hängender Zeiger). Alle Versuche, auf diese Referenz zuzugreifen, führen zu undefiniertem Verhalten. Dies geschieht in Ihrem Beispiel.

Die Referenz endet ihre Lebensdauer, bevor das Objekt, dann ... nichts passiert. Die Referenz verschwindet, das Objekt lebt weiter.

Die einzige Ausnahme, die ich oben gesprochen habe, ist, wenn eine const-Referenz mit einem sofortigen temporären Objekt initialisiert wird. In diesem Fall wird die Lebensdauer des temporären Objekts an die Lebensdauer der Referenz gebunden. Wenn die Referenz stirbt, stirbt das Objekt mit diesem

P.S.Sie verwenden den Begriff Bereich nicht korrekt. Der Bereich ist der Sichtbarkeitsbereich für einen Bezeichner. Scope selbst hat nichts mit der Lebensdauer von Objekten zu tun. Wenn etwas im Allgemeinen "Spielraum" lässt, wird etwas nicht zerstört. Es ist ein weit verbreitetes Missverständnis, den "Leaves Scope" -Beweis zu verwenden, um sich auf den Zerstörungspunkt eines Objekts zu beziehen.

+1

Danke für das Zeigen des _scope_missbrauchs - manchmal versucht, Terminologie in einer Sprache zu einer anderen zuzuordnen, produziert sehr seltsame Ergebnisse - aber diese ganze Frage bezieht sich auf Dinge, die passieren, wenn stack-allocated Objekt zerstört wird, wenn sein Bezeichner außerhalb des Bereichs wird. (Ja, der Speicher wird nur bei 'leave' freigegeben, aber der Destruktor sollte trotzdem aufgerufen werden, deshalb wird er zerstört.) – whitequark

+0

Enthält die Ausnahme diese Situation: const T & f() {T t; Rückgabe t;} const T & tt = f(); benutze tt hier? Danke – jean

+0

@jean: Nein, tut es nicht. – AnT

Verwandte Themen