Auslagern von Klassen in externe Dateien
-
Hallo alle Zusammen,
ich befasse mich erst seit Kurzem mit der OOP und habe bisher meine Klassendefinitionen und die Hauptfunktion immer in einer Datei gespeichert und mit minGW kompiliert. Da mein aktuelles Projekt jedoch bereits etwas größer ist, wollte ich die Klassen in externe Dateien auslagern. Folgende Dateien habe ich vorliegen:
bankkonto.cpp
#include <iostream> #include <string> #include "konto.h" #include "user.h" using namespace std; int main() { // main }konto.cpp
#include <iostream> #include <string> #include "konto.h" using namespace std; //================================================================================================================================== // Klasse konto //================================================================================================================================== /*********************************************************************************************************************************** * leerer Konstruktor ***********************************************************************************************************************************/ konto::konto() { kontonummer = "000000000"; blz = "00000000"; kontostand = 0; } /*********************************************************************************************************************************** * Konstruktor mit Parametern ***********************************************************************************************************************************/ konto::konto(string kontonummer_in, string blz_in, double kontostand_in) { kontonummer = kontonummer_in; blz = blz_in; kontostand = euroToCent(kontostand_in); } /*********************************************************************************************************************************** * Die Methode subtrahiert den als Parameter übergebenen Betrag vom aktuellen Kontostand. Dieser kann dabei nie * negativ werden. Als Rückgabewert wird der nach der Transaktion aktualisierte Kontostand zurückgegeben. ***********************************************************************************************************************************/ double konto::betragAuszahlen(double auszuzahlenderBetrag_in) { unsigned long int auszuzahlenderBetrag = euroToCent(auszuzahlenderBetrag_in); // Umrechnung in Centbetrag, um Rundungsfehler zu vermeiden if(auszuzahlenderBetrag >= 0) { if(auszuzahlenderBetrag >= kontostand) { kontostand = 0; } if(auszuzahlenderBetrag < kontostand) { kontostand -= auszuzahlenderBetrag; } } else { cerr << "Es kann kein negativer Geldbetrag ausgezahlt werden!" << endl; } return centToEuro(kontostand); } /*********************************************************************************************************************************** * Die Methode addiert den als Parameter übergebenen positiven Geldbetrag auf den aktuellen Kontostand. Der einzuzahlen * Geldbetrag ist dabei immer positiv. Als Rückgabewert wird der nach der Transaktion aktualisierte Kontostand zurückgegeben. ***********************************************************************************************************************************/ double konto::betragEinzahlen(double einzuzahlenderBetrag_in) { unsigned long int einzuzahlenderBetrag = euroToCent(einzuzahlenderBetrag_in); if(einzuzahlenderBetrag >= 0) { kontostand += einzuzahlenderBetrag; } else { cerr << "Es kann kein negativer Geldbetrag eingezahlt werden!" << endl; } return centToEuro(kontostand); } /*********************************************************************************************************************************** * Gibt den aktuellen Kontostand zurück. ***********************************************************************************************************************************/ double konto::gibKontostand() { return centToEuro(kontostand); } /*********************************************************************************************************************************** * Gibt die Kontonummer zurück. ***********************************************************************************************************************************/ string konto::gibKontonummer() { return kontonummer; } /*********************************************************************************************************************************** * Gibt die BLZ zurück. ***********************************************************************************************************************************/ string konto::gibBlz() { return blz; } /*********************************************************************************************************************************** * Dient zur Umrechnung eines Euro-Wertes z.B. der Form 3.95 € in einen Centbetrag, also z.B. 395 cent. Damit werden bei * der weiteren Verarbeitung Rundungsfehler durch den Umgang mit ganzzahligen Werten statt Fließkommazahlen vermieden. ***********************************************************************************************************************************/ unsigned long int konto::euroToCent(double eurowert) { return eurowert*100; } /*********************************************************************************************************************************** * Dient zur Umrechnung eines Cent-Werts in einen Euro-Wert. ***********************************************************************************************************************************/ double konto::centToEuro(unsigned long int centwert) { return centwert/100.0; } // end class kontokonto.h
/* * File: konto.h * Author: Lukas * * Created on 16. Mai 2015, 21:16 */ #ifndef KONTO_H #define KONTO_H #include <iostream> #include <string> using namespace std; //================================================================================================================================== // HEADER Klasse konto //================================================================================================================================== class konto { private: string kontonummer; string blz; unsigned long int kontostand; public: konto(); konto(string, string, double); double betragAuszahlen(double); double betragEinzahlen(double); double gibKontostand(); string gibKontonummer(); string gibBlz(); unsigned long int euroToCent(double); double centToEuro(unsigned long int); }; // end class konto #endif /* KONTO_H */user.cpp
#include <iostream> #include <string> #include "user.h" using namespace std; //================================================================================================================================== // Klasse user //================================================================================================================================== /*********************************************************************************************************************************** * leerer Konstruktor führt zu einer Fehlermeldung, da ein User stets genau spezifiziert sein muss. ***********************************************************************************************************************************/ user::user() { cerr << "Initialisieren Sie bitte beim Konstruktoraufruf alle Parameter des Objekts!" << endl; } /*********************************************************************************************************************************** * Konstruktor mit Parametern ***********************************************************************************************************************************/ user::user(int id_in, string loginName_in, string loginPW_in, string name_in, string adresse_in, int kontoAnzahl_in, konto **verknuepfteKonten_in) { id = id_in; loginName = loginName_in; loginPW = loginPW_in; name = name_in; adresse = adresse_in; kontoAnzahl = kontoAnzahl_in; // für die Speicherung der mit dem User-Account verknüpften Konten, wird dynamisch Speicher angefordert, in den dann die verknüpften // Konten in Form von Zeigern auf die entsprechenden Konten-Objekte gespeichert werden. So wird kein Speicher durch unnötige lokale Kopien // der Objekte verbraucht und es kann direkt über den User auf dem entsprechenden Konto operiert werden. verknuepfteKonten = new konto*[kontoAnzahl]; for(int i = 0; i < kontoAnzahl; i++) { verknuepfteKonten[i] = verknuepfteKonten_in[i]; // hier müssen nun die Adressen der als Referenzen übergebenen Kontoobjekte in das Feld gespeichert werden } } /*********************************************************************************************************************************** * Destruktor gibt dynamisch alloziierten Speicher wieder frei ***********************************************************************************************************************************/ user::~user() { delete [] verknuepfteKonten; verknuepfteKonten = NULL; } /*********************************************************************************************************************************** * Setzt einen neuen als Parameter übergebenen Namen, der beim Login eingegeben werden muss. ***********************************************************************************************************************************/ void user::setzeLoginName(string loginName_in) { loginName = loginName_in; } /*********************************************************************************************************************************** * Setzt ein neues als Parameter übergebenes Passwort, das beim Login eingegeben werden muss. ***********************************************************************************************************************************/ void user::setzeLoginPW(string loginPW_in) { loginPW = loginPW_in; } /*********************************************************************************************************************************** * Setzt einen neuen als Parameter übergebenen Namen des Users. ***********************************************************************************************************************************/ void user::setzeName(string name_in) { name = name_in; } /*********************************************************************************************************************************** * Setzt eine neue als Parameter übergene Adresse des Users. ***********************************************************************************************************************************/ void user::setzeAdresse(string adresse_in) { adresse = adresse_in; } /*********************************************************************************************************************************** * Gibt den Login-Namen zurück. ***********************************************************************************************************************************/ string user::gibLoginName() { return loginName; } /*********************************************************************************************************************************** * Gibt das Login-Passwort zurück ***********************************************************************************************************************************/ string user::gibLoginPW() { return loginPW; } /*********************************************************************************************************************************** * Gibt den Namen des Users zurück. ***********************************************************************************************************************************/ string user::gibName() { return name; } /*********************************************************************************************************************************** * Gibt die Adresse des Users zurück. ***********************************************************************************************************************************/ string user::gibAdresse() { return adresse; } /*********************************************************************************************************************************** * Gibt true zurück, wenn die als Parameter übergenen Strings mit dem Login-Namen und dem * Login-Passwort übereinastimmen. ***********************************************************************************************************************************/ bool user::pruefeLogin(string loginName_in, string loginPW_in) { if(loginName_in == loginName && loginPW_in == loginPW) return true; return false; } /*********************************************************************************************************************************** * Gibt einen Zeiger vom Typ konto auf eines der mit dem User verknüpften Konten zurück. Die * Auswahl des Kontos aus der Liste der verknütpfen Kontos erfolgt über den Parameter nr. ***********************************************************************************************************************************/ konto* user::gibVerknuepftesKonto(int nr) { return verknuepfteKonten[nr]; } /*********************************************************************************************************************************** * Listet alle mit dem User verüpften Kontos in einer Tabelle auf ***********************************************************************************************************************************/ void user::listeKontos() { cout << endl << "#################### Kontouebersicht ####################" << endl << endl; cout << "Nr. \t" <<"Kontonummer \t" << "BLZ \t\t" << "Kontostand" << endl << endl; for(int i = 0; i < kontoAnzahl; i++) { string kontonummer = verknuepfteKonten[i]->gibKontonummer(); string blz = verknuepfteKonten[i]->gibBlz(); double kontostand = verknuepfteKonten[i]->gibKontostand(); cout << i << " \t" << kontonummer << " \t" << blz << " \t" << kontostand << " Euro" << endl; } cout << endl << "#########################################################" << endl << endl; } /*********************************************************************************************************************************** * Listet alle Userdaten auf ***********************************************************************************************************************************/ void user::zeigeNutzerdaten() { cout << endl << "###################### Nutzerdaten ######################" << endl << endl; cout << "Login Name: \t\t" << loginName << endl; cout << "Login Password: \t" << loginPW << endl; cout << "Name: \t\t\t" << name << endl; cout << "Adresse: \t\t" << adresse << endl; cout << endl << "#########################################################" << endl << endl; } // end class useruser.h
/* * File: user.h * Author: Lukas * * Created on 16. Mai 2015, 21:17 */ #ifndef USER_H #define USER_H #include <iostream> #include <string> using namespace std; //================================================================================================================================== // HEADER Klasse user //================================================================================================================================== class user { private: int id; string loginName; string loginPW; string name; string adresse; konto **verknuepfteKonten; // Zeiger auf ein Zeiger-Feld auf variable Anzahl von Kontoobjekten int kontoAnzahl; public: user(); user(int, string, string, string, string, int, konto**); ~user(); void setzeLoginName(string); void setzeLoginPW(string); void setzeName(string); void setzeAdresse(string); string gibLoginName(); string gibLoginPW(); string gibName(); string gibAdresse(); bool pruefeLogin(string, string); konto* gibVerknuepftesKonto(int); void listeKontos(); void zeigeNutzerdaten(); }; // end class user #endif /* USER_H */Wenn ich alles in eine Datei verpacke, funktioniert das Kompilieren problemlos, nun habe ich das Ganze Projekt in Netbeans eingefügt, entsprechende Files angelegt und erhalte beim Kompilieren folgende Fehlermeldung:
"/C/MinGW/msys/1.0/bin/make.exe" -f nbproject/Makefile-Debug.mk QMAKE= SUBPROJECTS= .build-conf make.exe[1]: Entering directory `/g/Documents/eigene Dokumente/Studium/Bachelor MB/Semester 4/nach Modulen/Informatik/Rechner�bung/RUE5/Rechneruebung_5/Sonstiges/Bankkonto2/Bankkonto' "/C/MinGW/msys/1.0/bin/make.exe" -f nbproject/Makefile-Debug.mk dist/Debug/MinGW-Windows/bankkonto.exe make.exe[2]: Entering directory `/g/Documents/eigene Dokumente/Studium/Bachelor MB/Semester 4/nach Modulen/Informatik/Rechner�bung/RUE5/Rechneruebung_5/Sonstiges/Bankkonto2/Bankkonto' mkdir -p build/Debug/MinGW-Windows rm -f "build/Debug/MinGW-Windows/bankkonto.o.d" g++ -c -g -MMD -MP -MF "build/Debug/MinGW-Windows/bankkonto.o.d" -o build/Debug/MinGW-Windows/bankkonto.o bankkonto.cpp mkdir -p build/Debug/MinGW-Windows rm -f "build/Debug/MinGW-Windows/user.o.d" g++ -c -g -MMD -MP -MF "build/Debug/MinGW-Windows/user.o.d" -o build/Debug/MinGW-Windows/user.o user.cpp In file included from user.cpp:3:0: user.h:27:3: error: 'konto' does not name a type konto **verknuepfteKonten; // Zeiger auf ein Zeiger-Feld auf variable Anzahl von Kontoobjekten ^ user.h:31:50: error: 'konto' has not been declared user(int, string, string, string, string, int, konto**); ^ user.h:42:3: error: 'konto' does not name a type konto* gibVerknuepftesKonto(int); ^ user.cpp:23:118: error: 'konto' has not been declared user::user(int id_in, string loginName_in, string loginPW_in, string name_in, string adresse_in, int kontoAnzahl_in, konto **verknuepfteKonten_in) { ^ user.cpp: In constructor 'user::user(int, std::string, std::string, std::string, std::string, int, int**)': user.cpp:34:2: error: 'verknuepfteKonten' was not declared in this scope verknuepfteKonten = new konto*[kontoAnzahl]; ^ user.cpp:34:26: error: expected type-specifier before 'konto' verknuepfteKonten = new konto*[kontoAnzahl]; ^ user.cpp:34:26: error: expected ';' before 'konto' user.cpp: In destructor 'user::~user()': user.cpp:45:12: error: 'verknuepfteKonten' was not declared in this scope delete [] verknuepfteKonten; ^ user.cpp: At global scope: user.cpp:128:1: error: 'konto' does not name a type konto* user::gibVerknuepftesKonto(int nr) { ^ user.cpp: In member function 'void user::listeKontos()': user.cpp:140:24: error: 'verknuepfteKonten' was not declared in this scope string kontonummer = verknuepfteKonten[i]->gibKontonummer(); ^ make.exe[2]: *** [build/Debug/MinGW-Windows/user.o] Error 1 make.exe[2]: Leaving directory `/g/Documents/eigene Dokumente/Studium/Bachelor MB/Semester 4/nach Modulen/Informatik/Rechner�bung/RUE5/Rechneruebung_5/Sonstiges/Bankkonto2/Bankkonto' make.exe[1]: *** [.build-conf] Error 2 make.exe[1]: Leaving directory `/g/Documents/eigene Dokumente/Studium/Bachelor MB/Semester 4/nach Modulen/Informatik/Rechner�bung/RUE5/Rechneruebung_5/Sonstiges/Bankkonto2/Bankkonto' make.exe": *** [.build-impl] Error 2 BUILD FAILED (exit value 2, total time: 1s)Weiß jemand auf die Schnelle einen Rat, woran das Problem liegen könnte? MinGW gibt beim Kompilieren ähnliche Fehler aus, allerdings vermute ich, dass man dort die Header ohnehin manuell linken muss.
Besten Dank schonmal.
Grüße
Lukas
-
In user.h fehlt eine Deklaration der Konto-Klasse. Also konto.h includen; eine Vorwärtsdeklaration würde es in diesem Fall aber auch tu, da in user.h bloß Zeiger auf konto benutzt werden.
MinGW gibt beim Kompilieren ähnliche Fehler aus, allerdings vermute ich, dass man dort die Header ohnehin manuell linken muss.
Du schmeißt Begriffe wild durcheinander.
-
Hi,
in deiner user.h benutzt du bereits die Klasse konto. Deshalb müsstest du da noch die konto.h includieren, sonst weiß der Compiler nicht, was konto sein soll.
-
Vielen Dank! Darauf hätte ich auch selbst kommen können, aber manchmal sieht man einfach den Wald vor lauter Bäumen nicht.
Jedenfalls funktioniert es jetzt so wie es sollte.@SeppJ: Was genau meinst du damit, dass ich die Begriffe durcheinanderschmeiße? Ist es denn nicht der Linker, der die Header-Files (sowie auch die Systembibliotheken) während des Build-Vorgangs "zusammensucht" und damit letztlich sämtlichen Code für das eigentliche Umsetzen in Maschinencode bereitstellt? Oder habe ich da etwas durcheinandergebracht? (Du darfst mich gerne aufklären, bin wie gesagt noch am Anfang, was C++ anbelangt.)
-
L.B. schrieb:
@SeppJ: Was genau meinst du damit, dass ich die Begriffe durcheinanderschmeiße? Ist es denn nicht der Linker, der die Header-Files (sowie auch die Systembibliotheken) während des Build-Vorgangs "zusammensucht" und damit letztlich sämtlichen Code für das eigentliche Umsetzen in Maschinencode bereitstellt? Oder habe ich da etwas durcheinandergebracht? (Du darfst mich gerne aufklären, bin wie gesagt noch am Anfang, was C++ anbelangt.)
Der Linker sucht sich lediglich die kompilierten cpp Dateien zusammen und kombiniert sie mit externen Bibliotheken.
Header-Datein werden bereits vom Präprozessor in die Sourcedatein hineingecopypastet, da der Compiler zum kompilieren bereits die dorthin enthaltenen Deklarationen braucht.Zwei weitere Dinge, die mir beim durchscrollen aufgefallen sind:
- dein Defaultkonstruktor für user. Wenn du nicht willst, dass er aufgerufen wird, deklariere ihn nicht. So kommt die Fehlermeldung beim User, wenn der Programmierer einen Fehler gemacht hat. Ist er schlichtweg nicht vorhanden, kompiliert es erst gar nicht
- manuelle Speicherverwaltung. Du verwendest new[] und delete[]. Das ist insbesondere bei Anfängern nie notwendig. Die sind nämlich schwer richtig hinzubekommen (du machst es bspw. falsch) und führen zu manuellen Aufwand. Nutz einen std::vector von Zeigern auf die Kontos, dann musst du auch keinen Destruktor schreiben.Ein Lob allerdings, dass du Kontonummern als Strings und Kontostände als Integer speicherst.
-
Evtl. etwas OT, aber findest du nicht, das du es mit den Kommentaren im Quellcode etwas übertreibst ?