ERLEDIGT : Anfaengerfehler: Vererbeung - Objekt uebernimmt Aenderung nicht
-
Guten Tag!
Ich loese gerade eine Aufgabe fuer einen C++ Einsteigerkurs und habe soweit alles fertig (denke ich). Jedoch uebernimmt mein Objekt scheinbar keine Aenderungen.ich moechte dies kurz anreissen (ich hoffe ich bekomme das angemessen komprimiert).
Ich habe ganz oben eine Fach.hh in der Hierarchie:
Fach.hpp:
class Fach { private: int frei; int belegt; public: Fach(int fr); int get_frei(void) const; int get_belegt(void) const; void set_frei(int k); void set_belegt(int k); int tu_sache_rein(int menge); int hol_sache_raus(int menge); };Fach.cpp:
#include <iostream> #include "Fach.hpp" using namespace std; // Konstruktor Fach::Fach(int fr) : frei(fr), belegt(0) {} //getter int Fach::get_frei() const { return this->frei; } int Fach::get_belegt() const { return this->belegt; } //setter void Fach::set_frei(int k) { frei = k; } void Fach::set_belegt(int k) { belegt = k; } int Fach::tu_sache_rein(int menge) { if(menge >= 0){ if (menge > get_frei()){ cout << "Sie tun mehr rein als reingeht." << endl; int cache = get_frei(); set_belegt(cache + get_belegt()); set_frei(0); return cache; } else{ set_belegt(get_belegt() + menge); set_frei(get_frei() - menge); return menge; } } else{ cout << "Sie haben 0 oder weniger reingetan. Das macht kein Sinn." << endl; return 0; } }Den Rest habe ich mal weggelassen, da unerheblich. Nun habe ich viele Facherklassen die alle relativ gleich sind: Cola_Fach, Eier_fach usw. usw., die alle von Fach erben sollen, aber beim Konstruktor eine Beschraenkung bekommen
an einem Beispiel von Cola_Fach will ich das mal zeigen (Dies hat nur platz fuer 6 Flaschen):Cola_Fach.hpp:
class Cola_Fach : public Fach { private: public: Cola_Fach(); };Cola_Fach.hpp:
#include "Fach.hpp" #include "Cola_Fach.hpp" Cola_Fach::Cola_Fach() : Fach(6) {}Alle diese Faecher sind gebuendelt in einem Kuehlschrank, den ich beladen will:
hier mal nur mit einem Cola_Fach gezeigt (Danach sollen auch andere Faecher hinzu):Kuehlschrank.hpp :
#include "Fach.hpp" #include "Cola_Fach.hpp" class Kuehlschrank{ private: bool tuer_offen; Cola_Fach colafach; public: Kuehlschrank(void); Cola_Fach get_Cola_Fach() const; void showall(void); bool offen_oder_nicht(); void schliessen(void); void oeffnen(void); int tu_rein(Fach& fach,int menge); int hol_raus(Fach fach,int menge); };Kuehlschrank.cpp :
#include <iostream> #include "Kuehlschrank.hpp" using namespace std; //// Konstruktor Kuehlschrank::Kuehlschrank(void) { tuer_offen = false; Cola_Fach cf; colafach = cf; } Cola_Fach Kuehlschrank::get_Cola_Fach() const{ return colafach; } bool Kuehlschrank::offen_oder_nicht() { return tuer_offen; } void Kuehlschrank::showall() { cout << " Colaflaschen: " << colafach.get_frei() << " noch Platz, " << colafach.get_belegt() << "drinne" << endl; if(offen_oder_nicht()){ cout << " Unser Kuehlschrank ist offen" << endl; } else{ cout << "Unser Kuehlschrank ist geschlossen" << endl; } } int Kuehlschrank::tu_rein(Fach& fach, int menge){ if(offen_oder_nicht()){//ist offen? return fach.tu_sache_rein(menge); } else{ cout << "Die Tuer ist zu! Du kannst nichts reintun" << endl; return 0; } }Nun wollte ich einen Kuehlschrank machen und den befuellen (in einer main.cpp)
main.cpp:
#include "Kuehlschrank.hpp" int main(void) { Kuehlschrank k; k.oeffnen(); k.showall(); cout << k.tu_rein(k.get_Cola_Fach(),5) << endl; k.showall(); return 0; }Ausgabe:
der kuehlschrank ist nun offen ! Colaflaschen: 6 noch Platz, 0drinne Eier: 8 noch Platz, 0drinne Kaese: 50 noch Platz, 0drinne Butter: 500 noch Platz, 0drinne Unser Kuehlschrank ist offen 5 Colaflaschen: 6 noch Platz, 0drinne Eier: 8 noch Platz, 0drinne Kaese: 50 noch Platz, 0drinne Butter: 500 noch Platz, 0drinne Unser Kuehlschrank ist offen
-
Nur mal so nebenbei: wieso haben deine Header die Endung ".hh"? Das habe ich ja noch nie gesehen. Normalerweise nimmt man da ".h" oder ".hpp".

-
_matze schrieb:
Nur mal so nebenbei: wieso haben deine Header die Endung ".hh"? Das habe ich ja noch nie gesehen. Normalerweise nimmt man da ".h" oder ".hpp".

hab' ich rausgebuegelt und editiert
-
<ZEINSIERTER KÄÄÄSE>
//Edit 1: Soweit ich weis ist *.cc und *.hh völlig legitim!
Ich nutze ganz langweilig *.cpp und *.h//Edit 2: DU MUSST BEI get_Cola_Fach() eine Referenz zurückgeben!
Cola_Fach& get_Cola_Fach();
statt
Cola_Fach get_Cola_Fach();
Weil sonst in tu_rein bloss eine temporäre Kopie von Cola_Fach verändert wird und nicht die Instanz im Kühlschrank!!
-
Ach und bei tu_rein brauchst du auch eine Referenz als Argument!
//.h Cola_Fach& get_Cola_Fach(); int tu_rein(Fach& fach,int menge); //.cc Cola_Fach& Kuehlschrank::get_Cola_Fach(){ return colafach; } int Kuehlschrank::tu_rein(Fach& fach, int menge){ if(offen_oder_nicht()){//ist offen? return fach.tu_sache_rein(menge); } else{ cout << "Die Tuer ist zu! Du kannst nichts reintun" << endl; return 0; } }P.S: Warum schreibe ich so viel käse wenn ich eingeloggt bin? Mit Namen wie asldkhafkolhlsdköasd anonym ist alles besser

-
Habe nun alles von dir umgesetzt pigeon!
Exzellent so funktioniert es. Ich werde noch ca 10 min darueber nachdenken muessen warum, aber das wird schon.habe es oben reineditiert!
Nochmals Danke. Das lief genial
-
ancestral-knowledge schrieb:
Exzellent so funktioniert es. Ich werde noch ca 10 min darueber nachdenken muessen warum, aber das wird schon.
Eine Referenz ist einem Zeiger sehr ähnlich. Wenn du etwas mit einem Getter zurückgibts:
int klasse::get_schueler() const { return anzahl; }Bekommst du nur eine Kopie der Anzahl zurück. Das verändert niemals die Klasseninstanz! Daher kannst du auch hinter einen solchen getter "const" schreiben! (Was du auch tun solltest)
Wenn du nun eine Referenz zurück gibst, gibst du die Speicheradresse von "anzahl" in deiner Klasse zurück! KEINE KOPIE! Daher kannst du hinter einen solchen getter auch nicht const schreiben, da er ja die Klasse verändern kann! (ausser mit mutable aber das ist nun egal
)Probiere doch das alles mal mit Zeigern! Dann wird es möglicherweise klarer...

-
Habe ich getan:
Die Rueckgabetypen aus der Klasse Fach.hpp haben demnach einen konstanten Rueckgabetypen, da sie nur eine kopie erzeugen. Dies habe ich nochmal hinzugefuegt oben.Bei dem Kuehlschrank jedoch habe ich ja eine Referenz auf mein Cola_Fach, da ich genau das Cola_Fach der Instanz aendern will. So muss der Rueckgabetyp des Getters Cola_Fach& get_Cola_Fach(); auch eine Refernz sein, da ich ja auf die Adresse des Colafaches meiner Instanz will. Wenn ich diese habe muss ich diese auch an tu_rein(Fach& fach, int menge) uebergeben, damit ich nicht auf einer Kopie meines Faches arbeite, sondern auf dem Fach meiner Instanz.
Ich denke ich habe es verstanden.
Danke nochmals fuer die Erlaeuterungen
-
Nein.
Mach wenn schon
const Cola_Fach& get_Cola_Fach() const; // oder: Cola_Fach const& get_Cola_Fach() const; // ist genau dasselbesonst ist dein Setter nutzlos und du könntest gerade so gut einen
public-Member anbieten. Durch die Const-Referenz kopierst du das Original ebenfalls nicht, aber verhinderst, dass es vom Aufrufer geändert wird. Das sollte auch so sein bei Get-Methoden (Lesezugriff).
-
Nexus schrieb:
Nein.
Mach wenn schon
const Cola_Fach& get_Cola_Fach() const; // oder: Cola_Fach const& get_Cola_Fach() const; // ist genau dasselbesonst ist dein Setter nutzlos und du könntest gerade so gut einen
public-Member anbieten. Durch die Const-Referenz kopierst du das Original ebenfalls nicht, aber verhinderst, dass es vom Aufrufer geändert wird. Das sollte auch so sein bei Get-Methoden (Lesezugriff).Ich glaube das lasse ich lieber... habe einfach keine Ahnung was das nun machen soll. Habe es auch einfach mal kurz ausprobiert und dann bekomme ich irgendwelche Probleme bei tu_sache_rein
"error C2662: 'Fach::tu_sache_rein' : cannot convert 'this' pointer from 'const Fach' to 'Fach &'"Trotzdem Dank.
-
dann musst du das so übergeben:
klasse.tu_was(*this);
Wenn du das Objekt das hinter einem Zeiger steht (this ist ein Zeiger) willst, dann mach ein * davor. Und gib nicht so schnell auf!!
Und da deine Cola beim getter konstant zurückgegen wird (const Cola&) darst du auch keine Methoden auf sie anwenden die die Cola verändern ... da sie ja const(ant) ist...
-
Pigeon schrieb:
dann musst du das so übergeben:
klasse.tu_was(*this);
Wenn du das Objekt das hinter einem Zeiger steht (this ist ein Zeiger) willst, dann mach ein * davor. Und gib nicht so schnell auf!!
Und da deine Cola beim getter konstant zurückgegen wird (const Cola&) darst du auch keine Methoden auf sie anwenden die die Cola verändern ... da sie ja const(ant) ist...
Ich habe versucht eineige Sachen zu beherzigen aber langsam verstehe ich nichts mehr. Wenn eine Methode const ist, dann heisst das ja, dass man damit keine Membervariablen ändern kann.
Jetzt habe ich das versucht und mein Programm umgebaut: Ich dachte : "Nunja ich will ja mit get_Cola_Fach() tatsaechlich nichts veraendern und habe die Methode const gemacht wie editiert". Damit habe ich mich aber nun wieder an den Anfang katapultiert, denn nun aendert sich das Objekt wieder nicht....Das wird immer verwirrender...
-
ancestral-knowledge schrieb:
Damit habe ich mich aber nun wieder an den Anfang katapultiert, denn nun aendert sich das Objekt wieder nicht....
Das wird immer verwirrender...
Das Gefühl habe ich auch. Wolltest du vorhin mit der indirekt über die Get-Methode das Objekt ändern? Zum Beispiel indem du eine Referenz gespeichert hast und dann über diese zugegriffen hast?
-
was ich halt in der main versuche ist zur zeit (die methoden sind alle im erstn post):
k.showall(); Cola_Fach &cf = k.get_Cola_Fach(); cout << cf.get_belegt()<< endl; //liefert richtige anzahl 0 cout << cf.get_frei()<< endl; //liefert richtige anzahl 6 cout << k.tu_rein(cf,6) << endl;//liefert richtige anzahl 6 (ich tue 6 rein) k.showall();Dies liefertirgendwie
Colaflaschen: 6 noch Platz, 0 drinne Unser Kuehlschrank ist offen 0 6 6 Colaflaschen: 6 noch Platz, 0 drinne // hier muesste aber 0 noch platz und 6 drinne stehenBin drum und dran das einfach hinzuschmeissen. 12 h daran schon gesessen und nichts leuchtet irgendwie ein.
-
Da du von aussen ohnehin voll und ganz auf das Colafach zugreifen willst mach es doch einfach als public member!
Ansonsten nimmst du den getter: Cola_Fach& get_Cola_Fach(); Jedoch macht das wie Nexus schon schrieb ned allzu viel Sinn ...
class Kuehlschrank { /* bla bla blub */ public: Cola_Fach m_colafach; /*blub bla bla*/ };oder nicht?

-
Zu dem Problem mit der unerwarteten Ausgabe: Solchen Fehlern kommst du am besten auf die Spur, indem du den Debugger benutzt und schrittweise den Programm durchgehst. Dann überprüfst du jeweils die Variablen und vergleichst sie mit deiner Vorstellung, wie es sein sollte. So findest du Abweichungen relativ schnell heraus.
Alternativ kannst du das auch mit Debugging-Ausgaben (z.B.
std::cout) hinkriegen. Aber der Debugger ist schon sehr mächtig, sich damit auseinanderzusetzen kann sich schnell lohnen. Zudem brauchst du dabei nicht deinen Code zu ändern...
-
Pigeon schrieb:
//Edit 1: Soweit ich weis ist *.cc und *.hh völlig legitim!
Ich nutze ganz langweilig *.cpp und *.h.cpp mochte ich noch nie, das erinnert mich immer an cpp den C-Präprozessor. ".C" ist für C++ auch noch üblich, man sollte dazu sagen auf UNIX Systemen. Auf einem DOS hätte das reichlich Probleme verursacht.
-
~john schrieb:
.cpp mochte ich noch nie, das erinnert mich immer an cpp den C-Präprozessor.
Deswegen nimmst du die Endung nicht?

Ich finde den Präprozessor sehr interessant. Aber davon abgesehen ist .cpp wohl die am meisten verbreitete Endung für C++-Implementierungsdateien. Ich nehme selber immer .cpp, .hpp und .inl bei Template-Implementierungen.
-
DAnke an alle,
ich habe es nun einfach public gemacht, wie ihr gesagt habt. macht tatsaechlich gleich viel Sinn. nebenbei habe ich noch den Umgang mit dem Visual 2008 Debugger gelernt.. also breakpoints usw. immerhin ich denke das wird viele Fragen in Zukunft vermeiden.
Guten Abend