2013-04-24 7 views
60

Ich habe einen Test hier, aber die Ausgabe ist eine Schleife ohne Ende, ich weiß nicht warum.Unendliche Schleife im Konstruktor ohne für oder während

Eigentlich mache ich einen weiteren Test, aber als ich dies schrieb, ich verstehe nicht, wie die Schleife aufgetreten. Es wird wiederholt "ABC" ausgegeben.

#include <map> 
#include <string> 
#include <iostream> 

class test 
{ 
public: 
    std::map <int, int> _b; 
    test(); 
    test (std::map<int, int> & im); 
    ~test(); 
    }; 

test::test() 
{ 
    std::cout<<"abc"; 
    _b.clear(); 
    _b[1]=1; 
    test(_b); 
} 

test::test(std::map <int, int>& im) 
{ 
    std::cout<<im[1]; 
} 

test::~test() {}; 

int main() 
{ 
    test a; 
} 
+1

Es ist Rekursion wegen 'test (_b);' aber ich bin mir nicht sicher warum genau. – Roddy

+0

@ Roddy- Ich habe es gerade herausgefunden; Siehe meine Antwort für Details. – templatetypedef

+1

Gereinigte Version mit unwichtigen Zeug entfernt: http://ideone.com/z0yc7Q – Yakk

Antwort

94

Das hier Problem ist, dass der Compiler

test(_b); 

nicht als Code interpretiert, die _b, ein temporäres Objekt vom Typ erzeugt in Parameter test geben, sondern als variable Deklaration für ein variable _b vom Typ namens test, mit der Standardkonstruktor. Folglich sieht das wie ein Stück Code, das ein temporäres test Objekt unter Verwendung des zweiten Konstruktor erzeugt wird stattdessen rekursiv ein neues Objekt des Typs test und Aufrufe den Konstruktor ein anderes Mal zu schaffen.

Um dies zu beheben, können Sie die Variable einen expliziten Namen geben, wie

test t(_b); 

Dies kann als eine Variable vom Typ nur testt, mit dem Namen interpretiert werden initialisiert, um den zweiten Konstruktor.

Ich habe nie vorher gesehen, und ich habe seit Jahren in C++ wurde Programmierung. Danke, dass Sie mir noch ein Eckfall der Sprache gezeigt haben!

Für eine offizielle Erklärung: Gemäß der C++ 03 ISO-Spezifikation, § 6.8:

Es gibt eine Mehrdeutigkeit in der Grammatik Einbeziehung Ausdruck-Aussagen und Erklärungen: Eine Ausdruck-Anweisung mit einer Funktion im Stil explizite Typumwandlung (5.2.3) als äußerste linke subexpression aus einer Erklärung nicht zu unterscheiden sein kann, wo der erste declarator beginnt mit einem (. In diesen Fällen sind die Erklärung eine Erklärung.

(Hervorhebung von mir). mit anderen Worten, jedes Mal, C++ entweder als einen Ausdruck, der eine Aussage interpretieren könnte (die temporäre Objekt Besetzung) oder als Deklaration (einer Variablen), wählt sie die Deklaration aus, die C++ - Spezifikation gibt explizit

T (a);

Als Beispiel für eine Erklärung, nicht eine Besetzung von a etwas vom Typ T.

Dies ist C++ Most Vexing Parse - was aussieht wie ein Ausdruck wird stattdessen als Deklaration interpretiert. Ich habe den MVP schon einmal gesehen, aber ich habe ihn nie in diesem Zusammenhang gesehen.

Hoffe, das hilft!

+1

Danke, das ist überraschend schwer für Google. – Antimony

+0

Gute Antwort! Gibt es irgendwelche Spezifikationsreferenzen, die beweisen/widerlegen, dass der Compiler das richtig interpretiert? FWIW, codepad.org zeigte das gleiche Verhalten – Tom

+0

Ah, ich fand eine Erklärung. Es heißt "Most Vexing Parse". Übrigens, in C++ 11 können Sie das beheben, indem Sie es in einen 'test {_b}' umwandeln. (Klammern statt Klammern verwenden) http://en.wikipedia.org/wiki/Most_vexing_parse – Antimony

0

das Problem von Konstruktor wird wieder Sie den contructor Test Aufruf (_b)

Test :: test() {std :: cout < < "abc"; _ b.clear(); _b [1] = 1; Test (_b);}

hier ist das, was

jedes Mal passiert, rufen Sie Test (_b) es erste Anrufe Standardkonstruktors Test :: Test und es in schaltet Anrufe der Test (_b) und die Schleife geht weiter und weiter, bis der Stapel überläuft.

den Test (_b) aus dem Konstruktor Standard entfernen

+0

Warum ruft das den Standardkonstruktor auf? Ich dachte, das würde den impliziten Konvertierungskonstruktor 'test :: test (std :: map &) 'aufrufen, da er' _b' explizit übergibt. – templatetypedef

+0

Warum ruft es den Standardkonstruktor auf? C++ kettet nicht Konstruktoren. – Antimony

+0

In C++ sollten Sie keinen anderen Konstruktor aufrufen. Dies unterscheidet sich beispielsweise von Java. – OlivierD

0

ich mit den Besonderheiten der Standard nicht vertraut bin, aber es kann sein, dass in einem Konstruktor einen Konstruktor aufrufen nicht definiert ist. Als solche könnte es compilerabhängig sein. In diesem speziellen Fall verursacht es eine unendliche Rekursion des Standardkonstruktors, ohne jemals den Konstruktor mit dem map-Argument aufzurufen.

C++ FAQ 10.3 hat ein Beispiel mit einem Konstruktor, der zwei Parameter hat. Wenn Sie Ihrem zweiten Konstruktor einen int-Parameter hinzufügen, z. B. test(map, int), zeigt sich ein etwas normales Verhalten.

Für eine gute Form würde ich einfach test::test(std::map <int, int>& im) für test::testInit(std::map <int, int>& im) und test(_b) zu testInit(_b) ändern.

+0

Es ruft einen anderen Konstruktor, aber es ist nicht der gleiche Konstruktor wie zuvor. Ich verstehe nicht, warum das die unendliche Rekursion verursacht, die hier gezeigt wird. – templatetypedef

0

Ich bin mir ziemlich sicher, dass Sie nicht tatsächlich "Aufruf des Konstruktors" seit sie nicht direkt IIRC Callable sind. Die Legalese hatte mit Konstruktoren zu tun, die nicht benannte Funktionen sind - ich habe keine Kopie des Standards handlich, oder ich könnte es zitieren. Ich glaube, was Sie mit test(_b) tun, ist eine unbenannte ein temporäres erstellen, die den Standardkonstruktor wieder aufruft.

+0

Es wird kein unbenanntes temporäres erstellt und der Standardkonstruktor aufgerufen; Stattdessen erstellt es eine Variable mit dem Namen '_b' und ruft den Standardkonstruktor auf. Siehe meine Antwort für weitere Details. – templatetypedef

+0

Danke. Ich erinnerte mich, was es tat, nur nicht den genauen Grund. Ich hatte vergessen, wie verdreht die C/C++ Deklarationsregeln tatsächlich sind. –

Verwandte Themen