Exklimo Geschrieben 10. August 2011 Geschrieben 10. August 2011 (bearbeitet) Hab alles gemacht, Klassen angelegt usw. und es funktioniert im prinzip auch, nur wenn ich das verfeinern will klappts nicht so wie ich will... vermute daran ist for-if Schuld -.- Hier das Main Script: #include "kunde.h" #include "bank.h" #include <iostream> extern bank b; int main() { kunde k; b.init("boris", "1234", 1000); for (; { k.geldkarte_einschieben(); if (k.PIN_eingeben() == true) { if (k.betrag_waehlen() == true) std::cout << "Der gewuenschte Betrag wurde abgehoben." << std::endl; } [I]\\ Für den Fall das die Pin falsch eingegeben worden ist soll "Pin ist nicht korrekt" angezeigt werden\\[/I] if (k.PIN_eingeben() == false) std::cout << " Pin ist nicht korrekt." << std::endl; [I]\\ Für den Fall, dass das Konto nicht ausreichend gedeckt ist, soll dementsprechend der Text erscheinen\\[/I] { if (k.betrag_waehlen() == false) std::cout << "Ihr Konto ist nicht ausreichend gedeckt." << std::endl; } } }[/code] Was mach ich falsch? Bearbeitet 11. August 2011 von Klotzkopp Code-Tags eingefügt Zitieren
flashpixx Geschrieben 10. August 2011 Geschrieben 10. August 2011 Was mach ich falsch? Du möchtest zuerst einmal lernen Code-Tags zu verwenden und das Problem mit allen Informationen / allen relevanten Code-Auszügen so zu beschreiben, dass man damit etwas anfangen kann, denn die OOP in diesem Beispiel, vor allem die von Dir geforderte Verfeinerung kann man nicht nachvollziehen Zitieren
Klotzkopp Geschrieben 11. August 2011 Geschrieben 11. August 2011 Was mach ich falsch?Du rückst deinen Code nicht ordentlich ein. Das is so ein Durcheinander, dass man nicht erkennen kann, wo die if-Blöcke aufhören. Außerdem lieferst du mit "klappt nicht" eine völlig unzureichende Problembeschreibung. Ein grundsätzlicher Tipp: Zeilenkommentare leitet man mit // ein, nicht mit \\ Zitieren
Freeed91 Geschrieben 11. August 2011 Geschrieben 11. August 2011 ma ne andere Frage kurz^^ das ganze std::cout std::endl etc. macht man das in der Praxis heute so? kenne eigentlich nur unter den headern ein using namespace std; und dann entsprechend nur cout endl etc. Zitieren
Klotzkopp Geschrieben 11. August 2011 Geschrieben 11. August 2011 macht man das in der Praxis heute so?Kommt drauf an. kenne eigentlich nur unter den headern ein using namespace std;Das ist die Holzhammermethode, damit hebelst du den kompletten Namespace aus. Und der ist ja nicht zum Spaß da. Der Namespace hat den Sinn, Namenskonflikte zu vermeiden. Daher sollte man using-Direktiven sparsam einsetzen, und so lokal wie möglich halten. In Headerdateien beispielsweise haben using-Direktiven nichts verloren. Zitieren
Exklimo Geschrieben 10. Oktober 2011 Autor Geschrieben 10. Oktober 2011 Tut mir Leid, dass ich hier keine Rückmeldung mehr gegeben habe. Mir fällt es sehr schwer bei der Programmierung dran zu bleiben. Ich bin im 3. Lehrjahr zum IT Systemkfm. und stehe vor meiner Prüfung, daher muss ich es langsam mal raffen. Das mit der Auskommentierung war natürlich peinlich Also, OOP steht an... ich arbeite mit dem DevC++ Ziel ist ein Prorgramm mit den Funktionen eines Bankautomats. Die klassische Übungsaufgabe eben Mein Programm funktioniert jedoch einfach nicht, und ich kann mit der Fehlerbeschreibung nichts anfangen, vllt ihr??? bank.h #include <string> class bank { public: bool zugriff_ueberpruefen(std::string benutzername, std::string pin); bool geld_abheben(int betrag); private: std::string Benutzername; std::string PIN; int Kontostand; }; bank1.cpp #include "bank.h" void bank::init (std::string benutzername, std::string pin, int kontostand) { Benutzername = benutzername; PIN = pin; Kontostand = kontostand; } bool bank::zugriff_ueberpruefen(std::string benutzername, std::string pin) { if (Benutzername == benutzername && PIN == pin) { return true; } if (Benutzername != benutzername && PIN == pin) return false; } bool bank::geld_abheben(int betrag) { if (Kontostand >= betrag) { Kontostand -= betrag; return true; } return false; } Fehler: Zeile 5 (???) F:\Schule\AE\Objektorientiertes Programmieren_Dev_Ordner\Projekt 1\bank1.cpp no `void bank::init(std::string, std::string, int)' member function declared in class `bank' Kann mir das jmd erklären? Benötigt ihr noch den Rest von dem ganzen Programm? Im Moment geht es erstmal um diesen einen Fehler. Vielen Dank Und ich BLEIBE DRAN.... versprochen Eure Mühe ist nicht umsonst Zitieren
flashpixx Geschrieben 10. Oktober 2011 Geschrieben 10. Oktober 2011 no `void bank::init(std::string, std::string, int)' member function declared in class `bank' Lies bitte einmal die von mir hervorgehobenen Teile und schau Dir Deinen Code dazu an. Zusätzlich solltest Du wissen, dass Header die Deklaration und die CPP Datei die Implementation ist, d.h. sowohl Header wie auch CPP müssen konsistent sein Zitieren
DITTY Geschrieben 11. Oktober 2011 Geschrieben 11. Oktober 2011 (bearbeitet) Ohne meine Korrekturen zu kompilieren, folgendes: 1.) Rücke bitte ein! 2.) Mal sehen, obs Dir weiterhilft: no `void bank::init(std::string, std::string, int)' member function declared in class `bank' Was steht dort wohl geschrieben? Englisch ist zwar auch nicht meine Stärke, aber wer mal sich die Grundlagen zu Klassen bzw. OOP verinnerlicht hat, was Voraussetzung ist, sollte mit dieser Fehlermeldung etwas anfangen können. Ich will mal nicht so sein: Du implementierst outline eine Methode "init()" als Klassenmethode, korrekterweise auch Memberfunktion genannt, welche als Prototyp in der Klassendefinition gar nicht existiert. So wie ich das sehe, soll Deine init()-Funktion einiges Initialisieren. Hierfür verwendet man i.d.R. sogenannte KONSTRUKTOREN. Name eines Konstruktors ist gleich der Name der Klasse. Es gibt immer einen Standard-Konstruktor, selbst wenn man diesen nicht extra angibt. Konstruktoren haben keinen Rückgabedatentyp, auch nicht VOID und man kann Konstruktoren auch überladen. Ebenso kann man auch mehrere Konstruktoren definieren, welche sich dann aber in der Parameterliste unterscheiden müssen. Bsp.-Definition/-Deklaration für Deine Klasse: BITTE als Prototyp in Deiner Klasse im PUBLIC-Teil! bank(std::string benutzername, std::string pin, int kontostand); Dann die dazugehörige Implementierung außerhalb (ouline) der Klassendefinition: bank::bank(std::string benutzername, std::string pin, int kontostand) { Benutzername = benutzername; PIN = pin; Kontostand = kontostand; } Das Ganze kannste auch direkt inline innerhalb der Klasse im Public-Teil machen: bank(std::string benutzername, std::string pin, int kontostand) { Benutzername = benutzername; PIN = pin; Kontostand = kontostand; } Zu Deiner zugriff_ueberpruefen()-Funktion: bool bank::zugriff_ueberpruefen(std::string benutzername, std::string pin) { if (Benutzername == benutzername && PIN == pin) { return true; } if (Benutzername != benutzername && PIN == pin) { return false; } } Irgendwie haste da einen Denkfehler in der Funktion, die da ausgeführt werden soll. Was ist wenn Benutzername richtig ist, die Pin aber nicht? Dann würden beide IF-Abfragen nicht ausgeführt werden. Du musst schon alle Kombinationen berücksichtigen! Deine Funktion funktioniert an sich zwar, eleganter und logischer wäre aber gewesen: bool bank::zugriff_ueberpruefen(std::string benutzername, std::string pin) { if(Benutzername == benutzername && PIN == pin) { return true; } return false; } Vorausgesetzt, dass Deine Funktion nur dann Zugriffsmöglichkeit signalisieren soll, wenn sowohl Benutzername, als auch Pin richtig sind. Nach meiner Korrektur würden nämlich die anderen 3 möglichen Fälle zu einem imaginären "else" zusammengefasst werden, da ja nur der abgefragte Zustand die türe öffnen würde, also true liefern würde. Andere Zustände würden stets ein false liefern. Übrigens, noch ein Denkfehler: Was bringt mir Deine zugriff_ueberpruefen()-Funktion, wenn ich dennoch die geld_abheben()-Funktion ausführen kann? Du müsstest in Deine Klasse schon noch eine Status-Variable mitführen und diese in sämtlichen Zugriffs-Funktionen, wie geld_abheben() miteinbeziehen. Zu Deinem Main-Programm: a.) Ist "kunde" bei Dir überhaupt eine Klasse oder eine Structur? Die Kunde.h wollteste uns ja nicht offenbaren. Wenn es aber eine class/struct ist und Du dort nicht wie bei Deiner bank-class Anfänger-Fehler gemacht hast, will ich nichts gesagt haben. b.) Dir ist schon bewusst, dass Deine Schleife eine Endlosschleife ist? c.) "extern bank b;": Wozu "extern"? Ich meine, klar ist das nützlich, wenn mehrere CPP-Instanzen sich ein und dieselbe Variable teilen sollen, was aber hier nicht der Fall ist. Eine Klasseninstanz oder auch Objekt einer Klasse (=>OOP) legt man wie folgt an: [class] <Klassenname> <Bezeichner>; Nach Deinem Beispiel also so: class bank b; Wobei "class" optional ist, macht aber den Quellcode verständlicher, da man direkt sieht, welchen Ursprung und Typ das Objekt hat. Willste das als "extern" deklarieren: extern [class] bank b; d.) "k.geldkarte_einschieben();": Kein Problem, wenn es diese Funktion als Memberfunktion der Klasse bank, denn geben würde. Ich sehe gerade, dass es zu einer Klasse/Struktur "kunde" gehört??? Wozu? Man schiebt doch keine Karte in die Person, sondern in ein Gerät der Bank, auch Automat genannt. e.) "k.betrag_waehlen()": Dito f.) "k.betrag_waehlen()": Dito g.) "k.PIN_eingeben()": Dito und: wozu nochmal abfragen? Einzugebende Initialisierungswerte, u.a. die Pin, übergibste doch schon Deinem kunde-Objekt, was meiner Meinung nach aber keinen Sinn ergibt, da die PIN-Eingabe ausschließlich mit der Bank zu tun haben sollte. Aber auch allgemein: beim Anlegen eines Objekts mit direkter Werteübergabe, z.B. PIN, haste doch bereits die PIN "eingegeben". Wozu dann nochmal? Selbstverständlich spricht nichts gegen eine separate PIN-Eingabe-Funktion. h.) Ich denke mal, Dein Objekt "k" sollte ein Objekt der Klasse "bank" sein und nicht einer Klasse "kunde". i.) Deine Main-Funktion hat als Rückgabedatentyp "int", dann übergebe dieser am Ende auch ein INT-Wert, für gewöhnlich 0: return 0; Soll nicht heißen, dass du INT bei main() entfernen sollst, denn 1.) gehört sich das so und 2.) ist der Standardrückgabedatentyp, wenn kein anderer angegeben, bei Funktionen, u.a. bei main(), immer int. ----------------------------------- So, ich hoffe, dass ich Dir ein wenig weiterhelfen konnte. Wie gesagt, habs nicht getestet und Tipp-Fehler können auch mir passieren. Ne fertige Lösung werd ich Dir bei diesem einfachen Bsp. aber nicht machen. Einführungsunterricht, samt Material und / oder fertige Lösungen, wenn dann nur gegen Bares (PM an mich ). Grüße DITTY :) Bearbeitet 11. Oktober 2011 von DITTY Zitieren
Klotzkopp Geschrieben 11. Oktober 2011 Geschrieben 11. Oktober 2011 Es gibt immer einen Standard-Konstruktor, selbst wenn man diesen nicht extra angibt.Der Compiler erzeugt nur dann einen Default-Konstruktor, wenn der Benutzer überhaupt keinen Konstruktor definiert. man kann Konstruktoren auch überladen. Ebenso kann man auch mehrere Konstruktoren definieren, welche sich dann aber in der Parameterliste unterscheiden müssen.Letzteres ist das, was man Überladen nennt Das Ganze kannste auch direkt inline innerhalb der Klasse im Public-Teil machen: bank(std::string benutzername, std::string pin, int kontostand) { Benutzername = benutzername; PIN = pin; Kontostand = kontostand; }[/CODE] Kann er. Aber was wäre der Vorteil? Besser wäre hier übrigens die Verwendung einer Initialisierungsliste. Deine Main-Funktion hat als Rückgabedatentyp "int", dann übergebe dieser am Ende auch ein INT-Wert, für gewöhnlich 0: [CODE]return 0;Bei main darf man die return-Anweisung weglassen, das ist dann gleichbedeutend mit return 0; Zitieren
DITTY Geschrieben 12. Oktober 2011 Geschrieben 12. Oktober 2011 Hi, Der Compiler erzeugt nur dann einen Default-Konstruktor, wenn der Benutzer überhaupt keinen Konstruktor definiert. Wusste ich noch gar nicht. Wieder was gelernt. Kann man das denn irgendwo nachlesen (Quellen bitte)? Wie schauts mit dem Destruktor aus? Letzteres ist das, was man Überladen nennt Ja, meinte beim ersten eigentlich, das Mitgeben von Parametern. Kann er. Aber was wäre der Vorteil? Besser wäre hier übrigens die Verwendung einer Initialisierungsliste. Inline bringt selbstverständlich nicht wirklich einen Vorteil, technisch zumindest nicht. Übertreiben müssen wirs bei so einem einfachen KLEINEN Beispiel aber nicht. Gerade bei diesem Konstruktor bietet sich Inline Zwecks Lesbarkeit aber an. Wegen Initialisierungsliste: ich wollte ihn nicht überfordern. Natürlich bietet sich auch eine Initialisierungsliste an, finde ich aber ehrlich gesagt manchmal auch unübersichtlich, je nach Anzahl zu initialisierender Variablen. Bei main darf man die return-Anweisung weglassen, das ist dann gleichbedeutend mit return 0; Ach, man liest hier und da immer wieder Verschiedenes. Was hätte es denn technisch für eine Auswirkung, wenn man trotzdem "return 0" bei "int main(){}" mitführt? Ich machs halt immer mit. 2 Sekunden Tipparbeit hat noch keinen geschadet. Ebenso verschwinden so auch lästige Compiler-Warnungen. Grüße DITTY :D:D:D Zitieren
Klotzkopp Geschrieben 12. Oktober 2011 Geschrieben 12. Oktober 2011 Wusste ich noch gar nicht. Wieder was gelernt. Kann man das denn irgendwo nachlesen (Quellen bitte)?C++ Standard (1998 oder 2003, den neuesten habe ich noch nicht), Punkt 12.1.5. Wie schauts mit dem Destruktor aus?Wenn es keinen benutzerdefinierten Destruktor gibt, wird ein impliziter erzeugt. Punkt 12.4.3. Wegen Initialisierungsliste: ich wollte ihn nicht überfordern. Natürlich bietet sich auch eine Initialisierungsliste an, finde ich aber ehrlich gesagt manchmal auch unübersichtlich, je nach Anzahl zu initialisierender Variablen.Wenn du const-Member oder Referenzmember hast, geht es gar nicht ohne Initialisierungsliste. Ich finde, man sollte sich das gleich richtig angewöhnen. Ach, man liest hier und da immer wieder Verschiedenes.Standard (98/03), Punkt 3.5.5. Was hätte es denn technisch für eine Auswirkung, wenn man trotzdem "return 0" bei "int main(){}" mitführt?Keine. Wenn der Compiler am Ende der main-Funktion keine return-Anweisung findet, nimmt er return 0; an. Ich machs halt immer mit. 2 Sekunden Tipparbeit hat noch keinen geschadet. Ebenso verschwinden so auch lästige Compiler-Warnungen. Ein Compiler, der da warnt, ist kaputt . Ich hatte es auch nur angemerkt, weil ich den Eindruck hatte, dass du die fehlende Return-Anweisung als Fehler angesehen hast. Zitieren
DITTY Geschrieben 12. Oktober 2011 Geschrieben 12. Oktober 2011 (bearbeitet) Wenn du const-Member oder Referenzmember hast, geht es gar nicht ohne Initialisierungsliste. Ich finde, man sollte sich das gleich richtig angewöhnen. Ich weiß, ischweiß Da das aber nicht der Fall war, hab ichs sein gelassen. Insb. bei Vererbung machts Sinn, da Konstruktorinitialisierung. Dennoch, wenn nicht CONST- oder Referenzmember, bevorzuge ich den Weg über Konstruktor-Körper. Outline oder Inline, je nach Codeumfang für den/die Konstruktor(en). Scheint aber ne Frage des Geschmacks sein. Technisch sehe ich da keinen Unterschied (korrigiert mich, wenn ich falsch liege). Keine. Wenn der Compiler am Ende der main-Funktion keine return-Anweisung findet, nimmt er return 0; an. Gut, dachte ich mir schon. Aber das setzt wieder eine Compilerabhängigkeit voraus. Sicher, jeder gute Compiler unterstützt dies. Ich finde trotzdem, dass die eine kleine Zeile am Ende einer Funktion / der main()-Funktion nicht schaden kann. Findet ja nicht nur bei main() Anwendung, sondern auch bei anderen Funktionen. Hässlicher finde ich es, wenn jemand Funktionen mit Rückgabetyp, aber fehlender return-Anweisung schreibt. Deshalb wende ich das grundsätzlich bei allen Funktionen mit Datentyp !(void) an, die main()-Funktion eingeschlossen. Ein Compiler, der da warnt, ist kaputt . Ich hatte es auch nur angemerkt, weil ich den Eindruck hatte, dass du die fehlende Return-Anweisung als Fehler angesehen hast. Hm, finde ich nicht. Denn a.) ist es nur eine Warnung, keine Fehlermeldung und b.) erinnert es einem Programmierer immer wieder daran, dass es sich gehört ein RETURN zu werfen, wenn Rückgabedatentyp angegeben ist. Und nein, natürlich hab ich es nicht als Fehler verstanden und auch nicht angemerkt. Es wiedersprach einfach meinen Designprinzipien und sollte vielmehr ein gut gemeinter Tipp sein, kein MustHave. -------------------- Zu den Standard-Auszügen. Hab großen Dank. Ich hab mir jetzt aber angewöhnt, gleich im neuen 10-/11er-Standard zu proggen. Wird aber gewiss ähnlich oder gleich sein. Angesichts vieler gerade laufender Projekte komme ich leider nicht dazu, das nachzuschlagen, zumal es ja nicht falsch oder fehlerhaft ist, wenn man Konstruktoren, Destruktoren und return-Anweisungen grundsätzlich angibt. Grüße DITTY Bearbeitet 12. Oktober 2011 von DITTY Zitieren
Klotzkopp Geschrieben 12. Oktober 2011 Geschrieben 12. Oktober 2011 Technisch sehe ich da keinen Unterschied (korrigiert mich, wenn ich falsch liege).Der Unterschied ist der zwischen direkter Initialisierung und Default-Initialisierung mit anschließender Zuweisung. Je nach Typ des Members kann das ein großer Unterschied sein, beispielsweise wenn der Zuweisungsoperator sehr "teuer" ist. zumal es ja nicht falsch oder fehlerhaft ist, wenn man Konstruktoren, Destruktoren und return-Anweisungen grundsätzlich angibt.Bei Destruktoren muss ich da widersprechen, dazu habe ich zu oft gesehen, wie einer Klasse reflexartig ein virtueller Destruktor mit leerem Rumpf verpasst wurde. Eine Klasse sollte nur dann einen Destruktor bekommen, wenn sie ihn braucht. Und wenn sie ihn braucht, braucht sie auch einen Copy-Konstruktor und einen Copy-Zuweisungsoperator, oder muss unkopierbar gemacht werden. Destruktoren von Basisklassen sollten public virtual oder protected und nicht virtual sein. Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.