2017-08-27 1 views
0

Ist es nach dem C++ - Standard ein undefiniertes Verhalten, einen Verweis zu kopieren, bevor das Objekt initialisiert wird, auf das es verweist? Dies geschieht im folgenden Beispiel, in dem ich einen Verweis auf eine Elternklasse übergebe und erst danach den Wert des Objekts initialisiere, da der Aufruf des Elternkonstruktors in der Initialisierungsliste an erster Stelle stehen muss.Kopieren eines Verweises auf ein nicht initialisiertes Objekt in C++

#include <iostream> 

struct Object 
{ 
    int val; 
    Object(int i): val(i) {} 
}; 

struct Parent 
{ 
    Object& ref; 
    Parent(Object& i): ref(i){} 
}; 

struct Child : Parent 
{ 
    Object obj; 
    Child(int i): Parent(obj), obj(i) {} 
}; 

int main() 
{ 
    std::cout << Child(3).ref.val; 
} 

Hier wenn Elternteil mit Eltern initialisiert (obj), wird der Wert von obj wurde noch nicht initialisiert.

Dies kompiliert gut unter gcc, und ich bekomme eine korrekte Ausgabe, aber ich bin mir nicht sicher, ob die Standard-oder gute Kodierung Praxis davon abraten. Ist es ein undefiniertes Verhalten? Und wenn nicht, ist es eine schlechte Übung, die ich vermeiden sollte?

+2

Sie haben keinen nicht initialisierten Verweis, sondern einen Verweis auf ein nicht initialisiertes Objekt. Das unterscheidet sich von dem, was Sie aus dem Standard zitieren, und der Code ist in Ordnung, solange 'Eltern' die Referenz nicht verwendet, bis alles initialisiert ist. –

+0

Danke, ich habe den Titel aktualisiert, um Ihre Bemerkung zu reflektieren. Was Sie sagen, ist sinnvoll, da es sich um einen Zeiger auf ein nicht initialisiertes Objekt handelt. – Flynsee

+0

Hat also Bos Kommentar deine Frage beantwortet? Oder haben Sie bearbeitet, um einen Unterschied in Ihrer Frage zu klären? – Yunnosch

Antwort

2

Zunächst lassen Sie mich eine Sache klären. Ich bin mir nicht sicher, ob es sogar möglich ist, wörtlich eine Referenz zu kopieren.

int i = 10; 
int& ref = i;  // since this moment ref becomes "untouchable" 
int& alt_ref = ref; // actually, means int& alt_ref = i; 

denke ich, das gleiche passiert, wenn ref ein Mitglied einer Klasse ist und Sie eine Instanz dieser Klasse kopieren. Außerdem, wenn Sie näher auf Ihren Code schauen, Sie nicht einmal "eine Referenz kopieren", sondern eine Referenz mit nicht initialisierten (noch) Objekt initialisieren.

struct Parent 
{ 
    Object& ref; 
    Parent(Object& i): ref(i) { } 
}; 

struct Child : Parent 
{ 
    Object obj; 
    Child(int i): Parent(obj), obj(i) { } 
}; 

ist physikalisch äquivalent zu:

struct Child 
{ 
    Object& ref; 
    Object obj; 
    Child(int i): ref(obj), obj(i) { } 
}; 

Mit diesem wird gesagt, Ihre Frage eigentlich bedeutet:

Ist es nicht definiertes Verhalten zu eine Referenz vor initialisieren das Objekt initialisiert es ist etwa verweisen?

Hier ein Zitat von C++ Norm (§3.8.6 [basic.life/6]), die gibt möglicherweise die Antwort:

In ähnlicher Weise, bevor die Lebensdauer eines Objekts begonnen hat, aber nach Der Speicher, den das Objekt belegen soll, wurde zugewiesen, oder nach der Lebensdauer eines Objekts und vor der Speicherung des Objekts, das belegt oder freigegeben ist, darf nur ein glvalue verwendet werden, der sich auf das Originalobjekt bezieht in begrenzten Möglichkeiten. Für ein Objekt im Bau oder Zerstörung, siehe 12.7. Andernfalls bezieht sich ein solcher glvalue auf zugewiesenen Speicher (3.7.4.2), und die Verwendung der Eigenschaften des glvalue, die nicht von seinem Wert abhängen, ist wohldefiniert.

Und §12.7.1 [class.cdtor/1] sagt nur:

... mit Bezug auf jede nicht-statisches Element oder Basisklasse des Objekts vor dem Konstruktor beginnt Ergebnisse Ausführung in undefiniertem Verhalten.

§12.7.1 nur erwähnt „die sich auf Objekte Mitglieder“, also „auf das Objekt verweisen selbst“ fallen unter §3.8.6. Auf diese Weise kann ich feststellen, dass die Bezugnahme auf nicht initialisiertes (aber bereits zugewiesenes) Objekt wohldefiniert ist.

Wenn Sie irgendwelche Fehler sehen, lassen Sie mich bitte in den Kommentaren wissen. Fühlen Sie sich auch frei, diese Antwort zu bearbeiten.

Edit: Ich möchte nur sagen, dass eine solche Schlussfolgerung vernünftig scheint. Die Initialisierung eines Objekts kann seinen Speicherort im Speicher nicht ändern. Was ist schlecht beim Speichern der Referenz auf bereits zugewiesenen Speicher noch vor der Initialisierung?

+1

Der zweite Absatz ist der Kern der Sache und in der Tat die Quelle der Antwort. Die Antwort lautet: "Was Sie tun, ist in Ordnung, aber es wird brechen, wenn Sie das angesprochene Objekt verwenden". Außerdem wäre es sinnvoll, anstelle von Zahlen die Absätze nach Abschnittsheadern zu verwenden. Abschnittsnummern neigen dazu, sich ziemlich zu ändern, Abschnittsnamen nicht so sehr. So zum Beispiel [basic.life/6] anstelle von §3.8.6 (was in der kommenden C++ 17 §6.8.7 ist). – StoryTeller

+0

Vielen Dank. Wie du gesagt hast: "kann benutzt werden, aber nur in beschränkter Weise" scheint darauf hinzuweisen, dass außerhalb des §12.7.1 die Verwendung des Objekts in Ordnung ist. Und es ist sowohl vernünftig als auch in Bezug auf das Speichermanagement zu erwarten. – Flynsee

Verwandte Themen