2013-02-20 2 views
7

Ich lese denken in C++ Kapitel 6 Initialisierung & Bereinigung. Der Autor sagte, dass:Speicherzuweisung in C++

Es ist eigentlich wahrscheinlicher, dass der Compiler der Praxis in C von Zuteilung aller Speicher für einen Bereich an der Öffnung Klammer des dass Umfangs folgen. Es spielt keine Rolle, denn als Programmierer können Sie nicht auf den Speicher zugreifen, bis er definiert wurde. Obwohl der Speicher am Anfang des Blocks zugewiesen ist, tritt der Konstruktoraufruf nicht vor dem Sequenzpunkt auf, an dem das Objekt definiert ist, da der Bezeichner erst dann verfügbar ist. Der Compiler überprüft sogar stellen Sie sicher, dass Sie die Objektdefinition nicht dort setzen, wo die Sequenz Punkt nur bedingt durchläuft, wie in einem Schalter Anweisung oder irgendwo ein Goto kann darüber überspringen.

Und dann der Autor gibt ein Beispiel, wie folgend:

class X { 
public: 
    X(); 
}; 

X::X() {} 

void f(int i) { 
    if(i < 10) { 
    //! goto jump1; // Error: goto bypasses init 
    } 
    X x1; // Constructor called here 
jump1: 
    switch(i) { 
    case 1 : 
     X x2; // Constructor called here 
     break; 
    // case 2 : // Error: case bypasses init 
     X x3; // Constructor called here 
     break; 
    } 
} 

int main() { 
    f(9); 
    f(11); 
}///:~ 

Ich verstehe nicht, warum der obige Code in Ordnung ist? Nach meinem Verständnis, x2 kann Initialisierung umgangen werden, wenn i nicht 1 ist.

Supplement: „It'a eigentlich wahrscheinlicher, dass der Compiler der Praxis in C von Zuteilung aller Speicher für einen Bereich an der Öffnung Klammer dieses Bereichs folgen“

Dieser Satz verwirrte mich auch.

Laut der Beschreibung des Autors hat der Compiler bei der Eröffnung der switch bereits Platz für x2 und reserviert. Wenn dies der Fall ist, besteht die Möglichkeit, dass x2 nicht initialisiert wird (Fall 1 ist nicht erfüllt).

Antwort

2

C++ Objektinitialisierung und Destruktionssemantik sind vollständig sprungsicher (Exception-, Goto-, Switch- und Loop-Konstrukte enthalten).

(die einzige Ausnahme von der C-Standard-Bibliothek vererbt wird (setjmp/longjmp))

6,6 Sprunganweisungen

beim Ausgang aus einem Rahmen (aber erreicht), Objekte mit automatische Speicherdauer (3.7.3), die in diesem Umfang erstellt wurden, werden in umgekehrter Reihenfolge ihrer Konstruktion zerstört. [Hinweis: Für Provisorien siehe 12.2. -end note] Die Übertragung aus einer Schleife, aus einem Block oder hinter einer initialisierten Variablen mit automatischer Speicherdauer umfasst die Zerstörung von Objekten mit automatischer Speicherdauer, die an dem Punkt übertragen werden, der von, aber nicht am Punkt übertragen wird übertragen an. (Siehe 6.7 für die Übertragung in Blöcke). [Hinweis: Das Programm kann jedoch beendet werden (zB durch Aufruf von std :: exit() oder std :: abort() (18.5)), ohne Klassenobjekte mit automatischer Speicherdauer zu zerstören.-Ende note]

+0

Diese Regel war in C++ 03 und wahrscheinlich C++ 98. Und es bricht C-Code nicht, weil alle C++ - Standards eine Ausnahme bei der Initiierung zulassen lized Objekt hat einen C-kompatiblen Typ. – aschepler

+0

Ich bin mir nicht sicher, was Ihren Kommentar zum "Brechen von C-Code" hervorruft? Ja, intrinsics (PODs, ich bin mir nicht sicher, gerade jetzt) ​​sind ein anderes Biest. in der ganzen Sprache. Und, ja, wie ich explizit zeige, waren die Regeln bezüglich Sprünge hinter Initialisierer laxer vor C++ 11 – sehe

+0

C.1 handelt von Unterschieden zwischen C und C++, nicht von Unterschieden zwischen C++ 03 und C++ 11. C++ 11 C.1.5 ist keine Änderung; es ist identisch mit C++ 03 C.1.4. Sie scheinen über die Änderung zu 6.7p3 zu sprechen, die ja einige C++ 03 Code bricht. – aschepler

5

Nach meinem Verständnis kann x2 Initialisierung umgangen werden, wenn ich nicht ist 1.

Nein, entweder die case 1 ausgeführt wird, und x2 wird definiert und dann zerstört bei Das Ende des switch Block, oder kein Fall wird ausgeführt und der gesamte switch Block tut nichts, so x2 ist nicht im Bereich, so ist es nicht initialisiert, aber es kann auch nicht bezeichnet werden. Entweder existiert es und ist sicher zu benutzen, oder es existiert nicht.

+0

Danke Wakely. Eine weitere Frage, wenn ich Fall 2: auschecke, wird ein Fehler angezeigt. Warum? – Fihop

+1

Denn dann könnte der Kontrollfluss des Programms zu "case 2" springen und die Initialisierung von 'x2' umgehen, was in C++ nicht erlaubt ist, wie das Buch sagt _" Der Compiler überprüft sogar, dass Sie nicht setzen die Objektdefinition, wo der Sequenzpunkt sie nur bedingt durchläuft, etwa in einer switch-Anweisung oder irgendwo, wo ein goto überspringen kann. "_ –

+0

Hallo Janathan, ich habe die Frage bearbeitet und noch eine Frage gestellt. Kannst du mir noch einmal einen Gefallen tun? – Fihop

0

Das Problem damit ist, dass eine lokale initialisierte Variable zerstört wird, wenn Sie ihren Bereich verlassen. c + + denkt, switch, wenn, während, sind alle ihre eigenen Bereich in Bezug auf die Erstellung von Variablen.

+0

Ich habe meine Frage bearbeitet. Kannst du mir helfen, es anzuschauen? – Fihop

+0

Nun, jetzt hast du es sogar selbst gesagt!Ich verstehe nicht, was Sie nicht verstehen, es sieht es einfach als seinen eigenen Bereich - wenn Sie eine lokale Variable initialisieren (und nicht zuordnen) - wird es am Ende dieses Bereichs zerstört werden. – Infested

0

Nein, x2 kann nicht umgangen werden, auch wenn i1 ist. Das Problem mit der goto wäre, dass, wenn es genommen wurde, der Konstruktor für x1 nicht aufgerufen würde (erinnern Sie sich, der Konstruktor wird an der X x1; Anweisung "aufgerufen" - weil der Aufruf des Konstruktors über das Ziel des Sprungs ist und Sie würden an einem Punkt, wo x1 war noch im Rahmen.

Aber man kann nicht springen über die Linie X x2; und endet in einem Punkt, wo x2 ist noch in Umfang, so dass in Ordnung ist.

+0

Hallo Nik. Ich habe die Frage bearbeitet. Kannst du mir helfen, es anzuschauen? – Fihop