Klasse und Frage zum Konstruktor



  • Dudemeister schrieb:

    Versuch doch mal ne Klasse Auto zu schreiben, die einen Kilometerstand hat. Nun soll es möglich sein verschiedene Autos anzulegen, z.B. ein Neues mit Kilometerstand = 0, oder ein gebrauchtes mit beliebigem Kilometerstand.

    Ich nenne also die Klasse Automobil. Was ich dann aber nicht merh verstehe, ist, warum die Klasse einen Kilometerstand hat. Muss ich den Kilometerstand durch über eine Methode von "außen" übergeben? Oder wie stellst du dir das vor?



  • Naja, ein Auto hat meistens nen Kilometerstand. Nicht über eine Methode, der Kilometerstand soll im Konstruktor übergeben werden.

    So dass man schreiben kann:

    Automobil gebrauchtWagen(2000);
        Automobil neuWagen(0);
    

    Aber du solltest dir Konstruktoren allgemein nochmal anschauen. Such einfach Beispiele oder Tutorials im Netz.



  • Also, das hier sollte doch jetzt schon mal gar nicht mehr so schlecht sein, oder?

    #include<iostream>
    using namespace std;
    
    class Automobil
    {
    private:
    
    public:
    	Automobil()		//Konstruktor
    	{
    
    	}
    };
    
    int main()
    {
    	Automobil gebraucht(20000), neu(0);		//im Argument steht der Kilometerstand
    
    return 0;
    }
    

    Konstruktoren tragen anscheinend den gleichen Namen wie die Klasse selbst und haben keinen Rückgabetyp. So viel hab ich schon mal selbst herausgefunden. Was dieser Konstruktor nun aber in diesem konkreten Fall machen soll ist mir schleierhaft...



  • Die Parameter des Konstruktors und die beim Aufruf übergebenen Argumente müssen natürlich zusammenpassen.

    Aber ich weiss nicht ob das hier viel bringt, du solltest dir wirklich in Ruhe die Thematik durchlesen, dann musst du auch nicht andauernd grundlegendste Dinge fragen.



  • vip@r schrieb:

    Also, das hier sollte doch jetzt schon mal gar nicht mehr so schlecht sein, oder?

    Naja, stell dir folgendes vor:

    Ich du bist Autohändler und willst deine Autos jetzt im Programm darstellen. Ein Auto hat für dich folgende wichtige Eigenschaften:
    Farbe, Zählerstand, Kaputt

    Dann könntest du das so verwenden wollen:

    Auto deinAuto(1000, "Rot", false);
    //1000 km Zählerstand
    //Farbe ist Rot
    //Kaputt ist false, also nicht Kaputt
    
    Auto meinAuto(0, "Schwarz", false);
    
    cout<<"Mein Auto ist "<<meinAuto.getFarbe()<<" lackiert\n";
    cout<<"Dein Auto ist "<<deinAuto.getFarbe()<<" lackiert\n";
    
    fahrOffRoad(meinAuto);
    fahrOffRoad(deinAuto);
    //Funktion die mit x Prozentiger Wahrscheinlichkeit das Auto kaputt macht
    //und den Zählerstand um y Kilometer erhöht
    
    if(deinAuto.isKaputt()) {
      cout<<"Dein Auto ist Kaputt gegangen :)\n";
    }
    if(meinAuto.istKaputt()) {
      cout<<"Mein Auto ist Kaputt genagen :(\n";
    }
    
    cout<<"Die Zählerstande lauten:\n";
    cout<<"Mein Auto: "<<meinAuto.getZaehlerstand()<<" km\n";
    cout<<"Dein Auto: "<<deinAuto.getZaehlerstand()<<" km\n";
    

    Und dazu jetzt die Funktion fahreOffRoad() schreiben und die Klasse Auto implementieren. Das ist eine nette Übung 🙂



  • Hab die Übung mal ausprobiert, Shade of Mine. Bei mir funktionierts. Ob's toll programmiert ist, weiß ich nicht:

    #include<iostream>
    #include<ctime>
    using namespace std;
    
    class Auto
    {
    private:
    	char wagenfarbe[10];
    
    public:
    	int kilometerstand;
    	bool wagenstatus;
    
    	Auto(int zaehlerstand, char farbe[10], bool status)
    	{
    		kilometerstand = zaehlerstand;
    		for(int i=0; i<10; i++)
    		{
    			wagenfarbe[i] = farbe[i];
    		}
    		wagenstatus = status;
    	}
    
    	char *getFarbe()
    	{
    		return wagenfarbe;
    	}
    
    	bool istKaputt()
    	{
    		return wagenstatus;
    	}
    
    	int getZaehlerstand()
    	{
    		return kilometerstand;
    	}
    };
    
    Auto fahrOffRoad(Auto automobil)
    {
    	int prozent, zaehlerstand_erhoehung, zaehlerstand;
    
    	srand(time(NULL));
    	prozent = rand() % 100 + 1;
    
    	if(prozent > 50)
    	{
    		automobil.wagenstatus = true;
    	}
    
    	srand(time(NULL));
    	zaehlerstand_erhoehung = rand() % 100 + 1;
    
    	automobil.kilometerstand = automobil.kilometerstand + zaehlerstand_erhoehung;
    
    return automobil;
    }
    
    int main()
    {
    	Auto deinAuto(1000, "Rot", false);
    	//1000 km Zählerstand
    	//Farbe ist Rot
    	//Kaputt ist false, also nicht Kaputt (wenn false, dann Auto nicht kaputt)
    	Auto meinAuto(0, "Schwarz", false);
    
    	cout << "Mein Auto ist " << meinAuto.getFarbe() << " lackiert\n";
    	cout << "Dein Auto ist " << deinAuto.getFarbe() << " lackiert\n";
    
    	//Funktion die mit x Prozentiger Wahrscheinlichkeit das Auto kaputt macht
    	//und den Zählerstand um y Kilometer erhöht
    	meinAuto = fahrOffRoad(meinAuto);
    	deinAuto = fahrOffRoad(deinAuto);
    
    	if(deinAuto.istKaputt())
    	{
    		cout << "Dein Auto ist Kaputt gegangen :)\n";
    	}
    
    	if(meinAuto.istKaputt())
    	{
    		cout << "Mein Auto ist Kaputt gegangen :(\n";
    	}
    
    	cout<< "Die Zaehlerstaende lauten:\n";
    	cout<< "Mein Auto: " << meinAuto.getZaehlerstand() << " km\n";
    	cout<< "Dein Auto: " << deinAuto.getZaehlerstand() << " km\n";
    
    return 0;
    }
    


  • Mir fällt grad was ganz komisches auf. Wenn ich das Programm schrittweise durchdebugge, dann bekomm ich für die Kilometerzahlen unterschiedliche Werte; wenn ich das Programm aber selbstständig bis zum Ende laufen lasse, dann hab ich beim Wert für zaehlerstand_erhoehung und automobil.wagenstatus (somit respektive "prozent") jeweils die gleichen Werte.

    Und das ist immer so der Fall!

    Könnt ihr mir sagen was das soll?



  • Der Ansatz ist schon gut, aber:
    - Member-Variablen sollten privat sein und nur über Methoden der Klasse geändert werden
    - für wagenfarbe hätte ich einen std::string verwendet, für wagenstatus einen enum (oder einen anderen Namen)
    - die Parameter-Übergabe und Rückgabe hat etwas Java-artiges

    PS: Was die Zufallswerte angeht: srand() ruft man nur einmal au, nicht vor jeder Zuallszahl.



  • CStoll schrieb:

    Der Ansatz ist schon gut, aber:
    - Member-Variablen sollten privat sein und nur über Methoden der Klasse geändert werden

    Ich hab jetzt mal zwei neue Methoden eingefügt; changeWagenstatus und changeKilometerstand.

    CStoll schrieb:

    - für wagenfarbe hätte ich einen std::string verwendet, für wagenstatus einen enum (oder einen anderen Namen)

    Ich bin das noch so aus C gewohnt; sorry...

    CStoll schrieb:

    - die Parameter-Übergabe und Rückgabe hat etwas Java-artiges

    Was meinst du damit? Ich kenn Java nicht...

    CStoll schrieb:

    PS: Was die Zufallswerte angeht: srand() ruft man nur einmal au, nicht vor jeder Zuallszahl.

    srand hab ich jetzt nur einmal drin!

    Hier mein neuer Code:

    #include<iostream>
    #include<ctime>
    #include<string>
    using namespace std;
    
    class Auto
    {
    private:
    	string wagenfarbe;
    	int kilometerstand;
    	bool wagenstatus;
    
    public:
    	Auto(int zaehlerstand, string farbe, bool status)
    	{
    		kilometerstand = zaehlerstand;
    		wagenfarbe = farbe;
    		wagenstatus = status;
    	}
    
    	string getFarbe()
    	{
    		return wagenfarbe;
    	}
    
    	bool istKaputt()
    	{
    		return wagenstatus;
    	}
    
    	int getZaehlerstand()
    	{
    		return kilometerstand;
    	}
    
    	int changeKilometerstand(int zaehlerstand)
    	{
    		kilometerstand = zaehlerstand;
    
    		return kilometerstand;
    	}
    
    	int changeWagenstatus(bool status)
    	{
    		wagenstatus = status;
    
    		return wagenstatus;
    	}
    };
    
    Auto fahrOffRoad(Auto automobil)
    {
    	int prozent, zaehlerstand_erhoehung, zaehlerstand;
    
    	srand(time(NULL));
    	prozent = rand() % 100 + 1;
    
    	if(prozent > 50)
    	{
    		automobil.changeWagenstatus(true);
    	}
    
    	zaehlerstand_erhoehung = rand() % 100 + 1;
    	automobil.changeKilometerstand(zaehlerstand_erhoehung + automobil.getZaehlerstand());
    
    return automobil;
    }
    
    int main()
    {
    	Auto deinAuto(1000, "Rot", false);
    	//1000 km Zählerstand
    	//Farbe ist Rot
    	//Kaputt ist false, also nicht Kaputt (wenn false, dann Auto nicht kaputt)
    	Auto meinAuto(0, "Schwarz", false);
    
    	cout << "Mein Auto ist " << meinAuto.getFarbe() << " lackiert\n";
    	cout << "Dein Auto ist " << deinAuto.getFarbe() << " lackiert\n";
    
    	//Funktion die mit x Prozentiger Wahrscheinlichkeit das Auto kaputt macht
    	//und den Zählerstand um y Kilometer erhöht
    	meinAuto = fahrOffRoad(meinAuto);
    	deinAuto = fahrOffRoad(deinAuto);
    
    	if(deinAuto.istKaputt())
    	{
    		cout << "Dein Auto ist Kaputt gegangen :)\n";
    	}
    
    	if(meinAuto.istKaputt())
    	{
    		cout << "Mein Auto ist Kaputt gegangen :(\n";
    	}
    
    	cout<< "Die Zaehlerstaende lauten:\n";
    	cout<< "Mein Auto: " << meinAuto.getZaehlerstand() << " km\n";
    	cout<< "Dein Auto: " << deinAuto.getZaehlerstand() << " km\n";
    
    return 0;
    }
    

    Das Problem, welches ich vorhin geschildert habe, dass die Werte nur unterschiedlich sind, wenn ich das Programm schritweise durchmach, besteht immer noch. Ich verstehe das überhaupt nicht!



  • Ich hab jetzt mal zwei neue Methoden eingefügt; changeWagenstatus und changeKilometerstand.

    Ich hätte da eher Funktionen erzeugt, die das ausdrücken was mit dem Wagen passieren soll: void fahre(int strecke); bzw. void beschaedige(); / void repariere();

    vip@r schrieb:

    CStoll schrieb:

    - für wagenfarbe hätte ich einen std::string verwendet, für wagenstatus einen enum (oder einen anderen Namen)

    Ich bin das noch so aus C gewohnt; sorry...

    Und dann die manuelle Kopierschleife ohne Rücksicht auf Null-Terminatoren *tststs*

    CStoll schrieb:

    - die Parameter-Übergabe und Rückgabe hat etwas Java-artiges

    Was meinst du damit? Ich kenn Java nicht...

    Ich habe mich auch nicht intensiv mit Java beschäftigt, aber dort ist es afair üblich, daß die Objekte unveränderlich sind und bei Bedarf modifizierte Kopien zurückgeben. In C++ kenne ich das eher so, daß man die Objekte per Referenz übergibt und direkt beeinflußt:

    void fahrOffRoad(Auto& automobil)
    {
        int prozent, zaehlerstand_erhoehung;
    
        prozent = rand() % 100 + 1;
    
        if(prozent > 50)
        {
            automobil.beschaedige();
        }
    
        zaehlerstand_erhoehung = rand() % 100 + 1;
        automobil.fahre(zaehlerstand_erhoehung);
    }
    

    CStoll schrieb:

    PS: Was die Zufallswerte angeht: srand() ruft man nur einmal au, nicht vor jeder Zuallszahl.

    srand hab ich jetzt nur einmal drin!

    Dort wird es immer noch bei jedem Funktionsaufruf ausgeführt. Setz den Aufruf lieber an den Anang der main().



  • Dort wird es immer noch bei jedem Funktionsaufruf ausgeführt.

    Du meintest vorhin wohl das rand. Ich hab das srand gemeint; schön aneinander vorbei geredet...

    Aber wenn ich das rand an den Anfang der main schreibe, dann wird es ja nur einmal ausgeführt, oder? Ich möchte aber doch für die beiden Aufrufe der Funktion fahrOffRoad() unterschiedliche rand-Werte!

    Oder versteh ich da was grad nicht?

    Edit: Jetzt hab ich verstanden! Du meintest schon srand... srand soll tatsächlich nur 1x im Programm initialisiert werden! Die Zeit läuft ja weiter 🙂



  • vip@r schrieb:

    Dort wird es immer noch bei jedem Funktionsaufruf ausgeführt.

    Du meintest vorhin wohl das rand. Ich hab das srand gemeint; schön aneinander vorbei geredet...

    Ich meinte schon das srand() 😉

    Edit: Jetzt hab ich verstanden! Du meintest schon srand... srand soll tatsächlich nur 1x im Programm initialisiert werden! Die Zeit läuft ja weiter 🙂

    Wenigstens bei einem Thema ist der Groschen gefallen 😃



  • Hey Leute!

    Kann vielleicht nochmal jemand eine ähnliche "Übungsaufgabe" posten? Hat ja das letzte Mal recht geklappt. Würd mich freun!

    Ich hab hier mal eine Klasse schreiben wollen, die einen komplexen Datentyp zur Verfügung stellt. Hat aber nicht so ganz geklappt:

    #include<iostream>
    using namespace std;
    
    class data_komplex
    {
    private:
    
    	struct komplex
    	{
    		int real;
    		int imag;
    	};
    
    public:
    
    	data_komplex(komplex data)
    	{
    		data.imag = 0;
    		data.real = 0;		
    	}
    };
    
    int main()
    {
    	data_komplex data;
    
    	data_komplex(data);
    
    return 0;
    }
    


  • Hm, der Lernerfolg war wohl ... sagen wir mal bescheiden 🙂

    Um auf deinen zuletzt geposteten code einzugehen:
    Es gibt mehrere Fehler, die du allerdings eigentlich alle selber lösen können solltest bzw. erkennen solltest.
    Deine Aussage "Hat aber nicht so ganz geklappt" heisst wohl schlicht und einfach: compiliert nicht.

    Allerdings wird dein compiler dir sicherlich Fehlermeldungen präsentiert haben. Du solltest anfangen, diese zu lesen und vorallem auch zu verstehen.

    Vermutlich wird er erstmal meckern, dass kein Standardkonstruktor für die Klasse data_komplex vorhanden ist.
    Jetzt solltest du dir erstens überlegen, warum er überhaupt einen Standardkonstruktor erwartet, warum er keinen bereitstellt und dann schaust du dir deine Klasse an und überlegst, was für einen Konstruktor du tatsächlich hast.

    Nächster Schritt ist zu überlegen, wie man also ein data_komplex-Objekt erzeugt mit dem vorhandenen Konstruktor. Wenn du das versuchst, wirst du auf den nächsten Fehler stossen bzw. der compiler wird wieder meckern. Aber eins nach dem anderen.
    😃



  • Ich hab hier dann mal nochmal was verändert. Jetzt funktioniert das soweit.

    #include<iostream>
    using namespace std;
    
    class komplex
    {
    private:
    	int real;
    	int imag;
    
    public:
    	komplex()		//Konstruktor
    	{
    		real = 0;
    		imag = 0;
    	}
    
    	~komplex()		//Destruktor; was könnte der hier für eine Funktion haben?
    	{
    
    	}
    
    	void changeKomplex()
    	{
    		cout << "Neuer Realteil: " ; cin >> real;
    		cout << "Neuer Imaginärteil: "; cin >> imag;		
    	}
    
    	void getReal_Imag(komplex &zahl)
    	{
    		cout << zahl.real << "+" << imag << "i" << endl;
    	}
    };
    
    void showKomplex(komplex zahl)
    {
    	zahl.getReal_Imag(zahl);
    }
    
    int main()
    {
    	komplex data1, data2;		//Klassendeklaration
    
    	data1.changeKomplex();
    	data2.changeKomplex();
    
    	showKomplex(data1);
    	showKomplex(data2);
    
    return 0;
    }
    

    Ich frage mich nun nur schon die ganze Zeit, was der Destruktor in diesem Fall macht? Könnt ihr mir helfen? Ist das Programm soweit ok?



  • Ich wusste gar nicht, dass man bei einer so primitiven Klasse so viel falsch machen kann.



  • 314...

    Meinst du meinen neuen oder den alten Code?



  • vip@r schrieb:

    Ich hab hier mal eine Klasse schreiben wollen, die einen komplexen Datentyp zur Verfügung stellt.

    Es wirkt auf mich so, als wuerdest du versuchen eine Klasse zu schreiben ohne zu wissen was du genau damit machen willst. Meine "Aufgabe" vorhin ist den anderen Weg gegangen: ich habe ein Ziel und dort will ich hin. Die Klasse ist der Weg dorthin. Wenn du nun kein Ziel hast, dann wird die Klasse schwammig. Dann tust du dir schwer die Klasse richtig zu implementieren weil du selber nicht genau weisst was du tun sollst. Das Auto Beispiel war zB bis auf das srand (was ja mit der Klasse ansich nichts zu tun hat) ordentlich. Dein Komplex hier ist leider garnichts.

    Also definiere dir eine Aufgabenstellung:

    Schreib ein Programm, dass 2 Brueche einliest und diese dann miteinander multipliziert, dividiert, subtrahiert und addiert. Und gib alle 4 Ergebnisse in Bruchform aus. bzw. kannst du das natuerlich auch mit komplexen Zahlen statt Bruechen machen wenn du in der Mathematik fit genug bist.

    Im Prinzip spielt es keine grosse Rolle was du als Ziel definierst. Die Klasse ist dann der Weg dorthin. uU brauchst du auch mal mehrere Klassen. Aber wichtig ist: ein klares Ziel.



  • vip@r schrieb:

    Ich frage mich nun nur schon die ganze Zeit, was der Destruktor in diesem Fall macht? Könnt ihr mir helfen? Ist das Programm soweit ok?

    Theoretisch: Der Destruktor zerstört als erstes die Klassen-Elemente (indem er deren Destruktoren aufruft), danach die Basisklasse(n) (genauso) und führt anschließend den Code im Destruktor-Rumpf aus.
    Praktisch: Die Member zu zerstören ist trivial, Basisklasse hast du nicht - und der Rumpf ist auch leer - also macht dieser Destruktor *trommelwirbel* gar nichts. Überhaupt ist ein Destruktor mit leerem Rumpf in den meisten Fällen* unnötig - den kann der Compiler genausogut selber aufbauen, ohne sich auf deine Mithilfe zu verlassen.

    *der einzige Sinn, der mir auf Anhieb dafür einfällt, ist die Definition eines virtuellen Destruktors.



  • Mir ist schon klar, dass der Rumpf des Destruktors momentan noch leer ist und somit auch nichts zerstören kann. Aber er ist ja deshalb noch leer weil ich eben nicht wüsste, was ich da zerstören könnte. Deshalb ich gefragt 🙂


Anmelden zum Antworten