Warum ist der explizite Konstruktoraufruf illegal?



  • enum Color {herz, karo, pik, kreuz, joker};
    
    class Karte{
    	Color Farbe;
    public:
    	Karte(Color desColor); // Konstruktor
    	Color getColor();
    };
    
    Karte::Karte(Color desiredColor):Farbe(desiredColor){
    }
    
    Color Karte::getColor(){
    	return Farbe;
    }
    
    #include <iostream>
    #include <cstdlib>
    using namespace std;
    
    void printColor(Karte& card){
    	switch(card.getColor()){
    		case herz:
    			cout << " Herz\n";
    			break;
    		case karo:
    			cout << " Karo\n";
    			break;
    		case pik:
    			cout << " Pik\n";
    			break;
    		case kreuz:
    			cout << " Kreuz\n";
    			break;
    		case joker:
    			cout << " Joker\n";
    			break;
    	}
    }
    
    int main(){
    	Karte* Stapel;
    	Stapel = (Karte*) malloc(sizeof(Karte)*33);
    
    	for(unsigned i=0; i<32; i++){
    		switch(i/8){
    			case 0:
    				Stapel[i].Karte(herz); // expliziter Konstruktoraufruf (? klappt nicht)
    				printColor(Stapel[i]);
    				break;
    			case 1:
    				Stapel[i].Karte(karo);
    				printColor(Stapel[i]);
    				break;
    			case 2:
    				Stapel[i].Karte(pik);
    				printColor(Stapel[i]);
    				break;
    			case 3:
    				Stapel[i].Karte(kreuz);
    				printColor(Stapel[i]);
    				break;
    		}
    	}
    	Stapel[32].Karte(joker); // Expliziter Konstruktor Aufruf
    	printColor(Stapel[32]);
    
    	fgetc(stdin); // damit das Fenster nicht automatisch schliesst
    	return 0;
    }
    

    Wenn ich versuche, den Code zu kompilieren, steht immer da, dass der rechte Operand von dem .-Operator bei den expliziten Konstruktoraufrufen illegal ist.
    Das wundert mich ziemlich, schließlich ist es ja nur der Aufruf des Konstruktors, und auch der Parameter ist vom Typ Color. Sehr merkwürdig!

    Bitte um Hilfe, um das Rätsel aufzulösen! Danke!


  • Mod

    Der Name einer Klasse wird in ihren eigenen Namensraum injiziert, in Stapel[i].Karte bezeichnet Karte also die Klasse selbst - ein Typ, und das ist mit diesem Operator nicht zulässig. Konstruktoren haben keinen Namen und können daher nicht auf diese Weise aufgerufen werden. Für Fälle wie diese - ich gehe davon aus, dass das nur Studienzwecken dient, denn für solche Aufgaben sind eigentlich Container da - gibt es placement new.

    new(&Stapel[i]) Karte(herz);
    


  • Thx. Ich werde wohl vernünftiger Weise einen Konstruktor, der keine Parameter braucht, verwenden. Und dann eine Init() Funktion.
    Das scheint die Handhabung des new-Operators zu vereinfachen. Es ist ja nicht so, dass temporär irgendwo Karten erzeugt werden sollen.
    -> Zu Studienzwecken. Aber ich möchte einmal die abstrakten Dinge, die wir so durchmachen, in einem Freizeit-Projekt anwenden, also hab ich mir gedacht, ein Kartenspiel wär' eine gute Übung. Paradoxer Weise brauch ich jetzt keine Sortier-Algorithmen, sondern genau das Gegenteil: Einen Misch-Algorithmus, der möglichst Unordnung in den Kartenstapel bringen soll. Jetzt kann ich dazu <vector> nicht so gut gebrauchen, und muss mein Glück irgendwie mit einem Array versuchen, fürchte ich.
    Jedenfalls besten Dank noch einmal 🙂


  • Mod

    std::random_shuffle



  • unerfahren3 schrieb:

    Thx. Ich werde wohl vernünftiger Weise einen Konstruktor, der keine Parameter braucht, verwenden. Und dann eine Init() Funktion.
    Das scheint die Handhabung des new-Operators zu vereinfachen.

    Das vereinfacht die Sache keineswegs. malloc legt keine kartenobjekte an, sondern besorgt nur den Speicher. Streng genommen müsste man an dieser Stelle ein sogenanntes placement new verwenden. Etwa so:

    case 0:
                    new (&Stapel[i]) Karte(herz); // erzeugt ein Kartenobjekt
                    printColor(Stapel[i]);
    

    Vereinfachen kannst Du alles, mit Hilfe eines Containers - z.B. std::vector<Karte> karten.

    case 0:
                    karten.push_back( Karte(herz) ); // erzeugt ein Kartenobjekt
                    printColor(karten.back());
    

    normalerweise sollte so auch kein überflüssiges Kartenobjekt angelegt werden.

    unerfahren3 schrieb:

    Einen Misch-Algorithmus, der möglichst Unordnung in den Kartenstapel bringen soll. Jetzt kann ich dazu <vector> nicht so gut gebrauchen, und muss mein Glück irgendwie mit einem Array versuchen, fürchte ich.

    Mit dem bereits erwähnten std::random_shuffle kannst Du auch den vector<Karte> 'mischen'.

    Gruß
    Werner
    @Edit: ich hatte campers erste Antwort zu spät gesehen ...



  • Sorry, wenn ich schon wieder da bin, es scheint, ich hab ein Verständnisproblem bezüglich malloc() und new[], letzterem 'new' wird ja nachgesagt, dass es Konstruktoren, soferne vorhanden, automatisch aufruft, was malloc() eigentlich nicht tun sollte.
    malloc() sollte ja eigentlich nur den Speicher beschaffen, und das Objekt nicht erzeugen.

    Habe folgenden Code ausprobiert:

    class mystery{
    protected:
    	short demoValue;
    public:
    	mystery();
    	void setDemo(short someValue);
    	short getDemo();
    };
    
    mystery::mystery():demoValue(0){}
    
    void mystery::setDemo(short someValue){
    	demoValue = someValue;
    }
    
    short mystery::getDemo(){
    	return demoValue;
    }
    
    #include <stdlib.h>
    #include <stdio.h>
    
    int main(){
    	mystery* Feld;
    	Feld = (mystery*) malloc(sizeof(mystery)*3);
    
    	// Jetzt wird's mysterioes:
    	Feld[0].setDemo(5);  /* Feld[0] ist noch nicht einmal konstruiert,
    							sondern malloc hat ja lediglich den Speicher
    							dafuer angefordert.
    							Trotzdem kann man schon die Member-Funktion setDemo
    							aufrufen, so, als waere Feld[0] durch einen Kon-
    							struktor tatsaechlich erzeugt worden */
    	printf("%d", Feld[0].getDemo()); /* Und der Wert '5' ist auch
    									 problemlos in die Membervariable
    									 demoValue geschrieben worden */
    	fgetc(stdin);
    
    	return 0;
    }
    

    Weder habe ich hier explizit einen Konstruktor aufgerufen, noch habe ich new verwendet (wodurch ein Objekt implizit erzeugt werden sollte, laut meinem Buch). Es gibt weder einen Compiler-Fehler, noch tritt zur Laufzeit ein Fehler ein: Das Programm schreibt schön '5', so wie es sollte.
    Wie ist es möglich, dass man die Member-Funktionen von einem Objekt, das nicht einmal konstruiert wurde, sondern für das ja nur Speicher bereitgestellt wurde, problemlos aufrufen kann (!?). Wäre es vorstellbar, dass malloc() womöglich ebenfalls implizit den Konstruktor der Klasse mystery aufruft, genau so, wie es eigentlich nur new[] machen dürfte ?



  • mystery* Feld; 
        Feld = (mystery*) malloc(sizeof(mystery)*3);  //Feld wird in einen gültigen Bereich Zeigen
        Feld[0].getDemo(); // wird bestimmt nicht null zurueckgeben, weil der Kontruktor nicht aufgerufen wurde
    


  • unerfahren3 schrieb:

    Wie ist es möglich, dass man die Member-Funktionen von einem Objekt, das nicht einmal konstruiert wurde, sondern für das ja nur Speicher bereitgestellt wurde, problemlos aufrufen kann (!?).

    Der Standard sagt nicht, dass ein Methodenaufruf auf ein nicht konstruiertes Objekt ein sofortiges Programmende bewirkt, sondern "undefiniertes Verhalten".

    Du kannst dich nicht darauf verlassen, dass es schief geht, wenn du etwas falsch machst. Schon gar nicht, dass etwas Bestimmtes passiert.

    unerfahren3 schrieb:

    Wäre es vorstellbar, dass malloc() womöglich ebenfalls implizit den Konstruktor der Klasse mystery aufruft, genau so, wie es eigentlich nur new[] machen dürfte ?

    Nein.



  • std::malloc ist aus C-Zeiten, wo es keine Konstruktoren gab. Demnach => Nein, tut es nicht.

    Dass das geht, darf eigentlich nicht, denn der this-Zeiger wird nicht erstellt und damit besteht keine Möglichkeit auf Membervariablen zu zugreifen.



  • (D)Evil schrieb:

    Dass das geht, darf eigentlich nicht, denn der this-Zeiger wird nicht erstellt und damit besteht keine Möglichkeit auf Membervariablen zu zugreifen.

    Den this-Zeiger muss er auch nicht initialisieren, denn der wird bei nicht-statischen Funktionen als Parameter übergeben.



  • Soweit ich weiß , dass man malloc() nur in C verwendet und für C++ verwendet man new Typ[]....



  • Ist mir bekannt, das der übergeben wird. Dennoch muss der irgendwann erstmal erzeugt werden.

    Außerdem ... new ist ein operator, malloc eine Funktion! malloc gibt void* zurück => nicht typsicher, new wirft exception wenn es nicht geklappt hat usw.
    Also nimm new 😉



  • Den this-Zeiger "erzeugst" du ja in dem Moment indem du x->y(); aufrufst. Da ist x dann der this-Zeiger. Also eigentlich ist das: y(x);



  • Da hat Fellhuhn recht. Du kannst (theoretisch) ja auch static_cast<Klasse*>(0)->foo() machen, was zwar undefiniert ist, aber auf den meisten Plattformen auch nur Klasse::foo() mit this == 0 aufruft. Der This-Zeiger muss nicht "erzeugt" werden 😉


Anmelden zum Antworten