mit Klassen arbeiten - wer kann mir helfen?
-
Hallo!
Ich arbeite mich grad durch die "Klassen" und hab mal versucht, ein simples additionsprogramm zu schreiben.
#include <iostream> #include <cmath> using namespace std; class mathe { public: int zahl1; int zahl2; mathe() { zahl1 = 0; zahl2 = 0; } void ausgeben() { cout << zahl1 << endl; cout << zahl2 << endl; } void addieren(mathe z2) { zahl1+=z2.zahl2; } void ergebnis() { cout << zahl1 << endl; } }; int main() { mathe m; m.zahl1 = 7; m.zahl2 = 4; m.ausgeben(); m.addieren(zahl2); m.ergebnis(); return 0; }
Die Zahlen sollen also erst ausgegeben, dann addiert und dann soll das ergebnis ausgegeben werden. Leider scheint mein ansatz nicht so ganz richtig, aber ich denke mal, für Könner wie euch ist das kein Problem, ich würd mich jedenfalls über Hilfe freuen!
-
Wie wäre es mit
m.addieren(m.zahl2);
Das sollte es sein.
Beim addieren dann acuh
void addieren(int z2) { zahl1+=z2; }
-
lousypoetry: Irgendwie ist Deine Klasse nicht allzu praktisch/ logisch aufgebaut; schreib doch einfach eine kleine Klasse für rationale Zahlen oä, das ist eine etwas bessere Übung.
-
Irgendwie ist Deine Klasse nicht allzu praktisch/ logisch aufgebaut;
ja, das hab ich mir auch schon überlegt... richtig Sinn machts sie eigentlich nicht...
schreib doch einfach eine kleine Klasse für rationale Zahlen oä, das ist eine etwas bessere Übung.
eine Klasse für rationale Zahlen? hm, versteh ich nicht so ganz, kannst das mal n bisschen erklären?
-
Naja, ist eigentlich eh eine 08/15-Übungsaufgabe, du schreibst eine Klasse für Brüche und definierst alle nötigen Operatoren damit man damit vernünftig rechnen kann. (zB 2 Brüche addieren und dann mit einem int multiplizieren etc)
edit: Die Grundstruktur könnte zB so aussehen:
class Bruch { int zaehler, nenner; public: Bruch(const int Zaehler = 0, const int Nenner = 1) : zaehler(Zaehler), nenner(Nenner) {} ~Bruch() {} };
Sowas ähnliches war IIRC auch Teil von "Thinking In C++" (vermutlich eher Vol1
) oder "Effective C++", beides äußerst empfehlenswerte Bücher! *zaunpfahlwink*
-
Naja, ist eigentlich eh eine 08/15-Übungsaufgabe, du schreibst eine Klasse für Brüche und definierst alle nötigen Operatoren damit man damit vernünftig rechnen kann. (zB 2 Brüche addieren und dann mit einem int multiplizieren etc)
hm, hab mein klassen-additions-programm
versucht in ein klassen-grundrechenarten-programm zu verwandeln und es klappt auch soweit. trotzdem ist OOP noch sehr ungewohnt, da ich doch geneigt bin eher mit funktionen zu arbeiten.
Sowas ähnliches war IIRC auch Teil von "Thinking In C++" (vermutlich eher Vol1) oder "Effective C++", beides äußerst empfehlenswerte Bücher! *zaunpfahlwink*
Jau, werd ich mir mal ansehen, gute Litaratur kann ja nicht schaden
PS: IIRC?
-
lousypoetry schrieb:
hm, hab mein klassen-additions-programm
versucht in ein klassen-grundrechenarten-programm zu verwandeln und es klappt auch soweit. trotzdem ist OOP noch sehr ungewohnt, da ich doch geneigt bin eher mit funktionen zu arbeiten.
Dafür ist eine Klasse ja auch nicht sinnvoll, was hätte das denn für einen Vorteil?
Versuche eine Klasse für rationale Zahlen zu basteln, dann wirst Du den Sinn von Klassen eher erkennen!
Wenn Du Probleme dabei hast den Sinn von OOP zu verstehen lies doch einfach mal Marc++us' Buch "OOP für Dummies":
Objektorientierte Programmierung für Dummies | ISBN: 3826629841
-
Richtig, eine Klasse "Mathe" ist absoluter quatsch.
-
Richtig, eine Klasse "Mathe" ist absoluter quatsch.
kritik ist gut und immer willkommen. aber vielleicht gehts n stück netter?
ehrlich gesagt check ich aber den Unterschied nicht, ob ich nun eine klasse schreib mit der man brüche addiert, dividiert,... oder ob ich eine klasse schreib mit der man zahlen addiert, dividiert,... Mir erscheint beides nicht wirklich sinnvoll. Hm, vielleicht find ich ja im Netz was Gutes zum nachlesen, denn so wirklich will das alles nicht in meinen kopf...
-
lousypoetry schrieb:
ehrlich gesagt check ich aber den Unterschied nicht, ob ich nun eine klasse schreib mit der man brüche addiert, dividiert,... oder ob ich eine klasse schreib mit der man zahlen addiert, dividiert,... Mir erscheint beides nicht wirklich sinnvoll. Hm, vielleicht find ich ja im Netz was Gutes zum nachlesen, denn so wirklich will das alles nicht in meinen kopf...
ganz einfach: Du schreibst keine Klasse, mit der man Brüche addiert, subtrahiert usw. sondern du schreibt eine Klasse, die Brüche repräsentiert. So dass Bruch zu einem neuen Datentyp wird. Beispiel:
// nur skizziert Bruch b1, b2; cout << "Geben sie einen Bruch ein: "; cin >> b1; cout << "Geben sie noch einen Bruch ein: "; cin >> b2; cout << "Die Summe dieser beiden Brüche ist " << b1+b2 << endl;
Jetzt müsste auch klar sein, warum man keine Klasse braucht, die Zahlen repräsentiert. Die gibt es schon, in Form der primitiven Datentypen int, float usw. OK: Das sind keine Klassen, sondern eben primitive Typen, aber das macht hier nchts. Klassen sind nur eine Art selbstdefinierter Datentyp.
[ Wenn du dich mal mit OOP beschäftigst wirst du feststellen, dass die dort vorkommenden Klassen recht gute Übereinstimmungen mit den C++-Klassen haben. ]
-
void mathe() { zahl1 = 0; zahl2 = 0; }
void vergessen.
-
seit wann haben konstruktoren nen rückgabetyp?
-
ganz einfach: Du schreibst keine Klasse, mit der man Brüche addiert, subtrahiert usw. sondern du schreibt eine Klasse, die Brüche repräsentiert. So dass Bruch zu einem neuen Datentyp wird.
ah, ich muss sagen, langsam aber sicher beginne ich zu verstehen (hoff ich mal ;)) hm, ich glaub ich werd mir das mit nmans "Bruechen" mal zu Herzen nehmen und versuchen... auch wenn ich das schlimmste befürchte. Danke für die Hilfe!
-
Schrei einfach, wenn du nicht mehr weiter weist. Vorab: wenns ans Kürzen geht,
schau dir mal Euklids Algorithmus zum finden des ggT (einfach googln, findest du bestimmt)
-
So ist's eleganter und schneller:
#include <iostream> #include <cmath> using namespace std; class mathe { public: int zahl1; int zahl2; mathe() { zahl1 = 0; zahl2 = 0; } void ausgeben() { cout << zahl1 << endl; cout << zahl2 << endl; } void addieren(int *z2) { zahl1+=*z2; } void ergebnis() { cout << zahl1 << endl; } }; int main() { mathe m; m.zahl1 = 7; m.zahl2 = 4; m.ausgeben(); m.addieren(&m.zahl2); m.ergebnis(); return 0; }
-
Schrei einfach, wenn du nicht mehr weiter weist. Vorab: wenns ans Kürzen geht, schau dir mal Euklids Algorithmus zum finden des ggT (einfach googln, findest du bestimmt)
danke für den Tipp!
-
so, ich hab mir mal Gedanken über Bruchrechnung gemacht und hab auch versucht eine Klasse für Brüche zu schreiben. Vorab: das Programm funktioniert soweit und rechnet auch korrekt, trotzdem hab ich das Gefühl, dass es nicht wirklich objektorientiert programmiert ist, weil man es glaub ich genauso gut auch mit Funktionen hätte lösen können:
ich poste es einfach mal und wer Lust (und die Nerven dazu hat, ist nämlich etwas länger und sicherlich geht es auch kürzer) kann es sich ja mal anschauen und mir vielleicht Verbesserungen (sicherlich;)), Hinweise, Fehler usw. sagen, wäre sehr nett!#include <iostream> #include <string> using namespace std; class bruch { public: int zaehler; int nenner; int zaehler2; int nenner2; int erg_zaehler; int erg_nenner; bruch(); //Konstruktor void einlesen(); void einlesen2(); void multiplizieren(int zx, int nx); void dividieren(int zx, int nx); void addieren(int zx, int nx); void subtrahieren(int zx, int nx); void ausgeben(); }; bruch::bruch() { zaehler = 0; nenner = 0; zaehler2 = 0; nenner2 = 0; erg_zaehler = 0; erg_nenner = 0; } void bruch::einlesen() { cout << "\nGeben Sie den 1.Bruch ein: "; string eingabe; cin >> eingabe; //Anzahl der Zeichen int anz = 0; for(int i=0; i<eingabe.size(); i++) { anz++; } //Position des Bruchstrichs suchen int pos = eingabe.find("/", 0); string strZaehler = eingabe.substr(0,pos); string strNenner = eingabe.substr(pos+1, anz); //string in int umwandeln zaehler = atoi(strZaehler.c_str()); nenner = atoi(strNenner.c_str()); } void bruch::einlesen2() { cout << "\nGeben Sie den 2.Bruch ein: "; string eingabe; cin >> eingabe; int anz = 0; for(int i=0; i<eingabe.size(); i++) { anz++; } int pos = eingabe.find("/", 0); string strZaehler2 = eingabe.substr(0,pos); string strNenner2 = eingabe.substr(pos+1, anz); zaehler2 = atoi(strZaehler2.c_str()); nenner2 = atoi(strNenner2.c_str()); } void bruch::multiplizieren(int zx, int nx) { erg_zaehler = zaehler * zx; erg_nenner = nenner * nx; } void bruch::dividieren(int zx, int nx) { erg_zaehler = zaehler * nx; erg_nenner = nenner * zx; } void bruch::addieren(int zx, int nx) { if(nenner == nenner2) //wenn gleicher Nenner { erg_zaehler = zaehler + zx; erg_nenner = nenner; } else //wenn ungleicher Nenner { erg_nenner = nenner*nenner2; erg_zaehler = (zaehler*nenner2) + (zaehler2*nenner); } } void bruch::subtrahieren(int zx, int nx) { if(nenner == nenner2) { erg_zaehler = zaehler - zx; erg_nenner = nenner; } else { erg_nenner = nenner*nenner2; erg_zaehler = (zaehler*nenner2) - (zaehler2*nenner); } } void bruch::ausgeben() { int i = 0; int rest[100]; rest[0] = erg_nenner; rest[1] = erg_zaehler; if(erg_zaehler <= erg_nenner) { for(i=0; i<=100; i++) //kürzen { rest[i+2] = rest[i]%rest[i+1]; if(rest[i+2] == 0) { erg_zaehler /= rest[i+1]; erg_nenner /= rest[i+1]; break; } } cout << "\nErgebnis: " << erg_zaehler << "/" << erg_nenner << endl; } else { // umwandeln int zahl = erg_zaehler/erg_nenner; int misch = erg_zaehler-(zahl*erg_nenner); erg_zaehler = misch; for(i=0; i<=100; i++) //kürzen { rest[i+2] = rest[i]%rest[i+1]; if(rest[i+2] == 0) { erg_zaehler /= rest[i+1]; erg_nenner /= rest[i+1]; break; } } cout << "\nErgebnis: " << zahl << " " << erg_zaehler << "/" << erg_nenner << endl; } } int main() { cout << "----------------------------" << endl; cout << "Brueche addieren <1>" << endl; cout << "Brueche subtrahieren <2>" << endl; cout << "Brueche multiplizieren <3>" << endl; cout << "Brueche dividieren <4>" << endl; while(true) { cout << "\nBitte waehlen Sie einen Menuepunkt: "; int wahl; cin >> wahl; bruch b; switch(wahl) { case 1: b.einlesen(); b.einlesen2(); b.addieren(b.zaehler2, b.nenner2); b.ausgeben(); break; case 2: b.einlesen(); b.einlesen2(); b.subtrahieren(b.zaehler2, b.nenner2); b.ausgeben(); break; case 3: b.einlesen(); b.einlesen2(); b.multiplizieren(b.zaehler2, b.nenner2); b.ausgeben(); break; case 4: b.einlesen(); b.einlesen2(); b.dividieren(b.zaehler2, b.nenner2); b.ausgeben(); break; default: cout << "Ungueltige Eingabe!" << endl; } cout << "\nMoechten Sie noch eine Rechnung durchfuehren?(y/n) "; string neu; cin >> neu; if(neu == "y") continue; else { cout << "Das Programm wird beendent!" << endl; break; } } return 0; }
PS: die "Menüstruktur" ist erst mal ein Mittel zum Zweck...
-
Kritikpunkte:
- public-Variablen. Ein Bruch hat eine Invariante (eine Bedingung die für ein gültiges Objekt immer zutreffen muss), nämlich dass der Nenner nicht 0 ist. Ich würde auch noch hinzufügen, dass der Bruch immer gekürzt ist. Wenn du mit public-Variablen den direkten Zugriff auf Zähler und Nenner zuläßt, kannst du die Invariante nicht aufrechterhalten.
Schlussfolgerung: Zaehler und Nenner sollten private sein ("Kapselung") - zuviele Variablen. Ein Bruch hat *einen* Zähler und *einen* Nenner, nicht jeweils 3.
- einlesen und ausgeben gehören nicht in die Klasse
- hier bietet sich Operatorüberladung an, aber es kann natürlich gut sein, dass du dich damit noch nicht beschäftigt hast.
wenn man das beherzigt, sieht die Klasse ungefähr so aus:
class Bruch { int zaehler, nenner; void kuerzen() // reicht als private Funktion { /* Euklids Algorithmus zur GGT-Berechnung anwenden, Zähler und Nenner durch ggt teilen */ } public: Bruch(int z = 0, int n = 1) { assert(n != 0); // oder eine andere Art den Fehler abzufangen kuerzen(); } int get_zaehler() { return zaehler; } int get_nenner() { return nenner; } /* Keine set-Methoden */ Bruch& addieren(const Bruch& other) { /* additionsalgorithmus implementieren */ kuerzen(); return *this; } // ... }; void einlesen(Bruch& br) { /* wie bei dir ... */ br = Bruch(z, n); } void ausgeben(Bruch const& br) { cout << br.get_zaehler() << '/' << br.get_nenner(); } Bruch addiere(Bruch const& br1, Bruch const& br2) { Bruch ergebnis = br1; ergebnis.addieren(br2); return ergebnis; } // ... int main() { Bruch a(2, 3), b(20, 2); Bruch c = addiere(a, b); ausgeben(c); }
Das ist schon gut, aber nocht nicht ganz das gelbe vom Ei. Dafür würde man aber Operatorüberladung brauchen. Ich deute mal an, wie das dann aussehen würde ...
Bruch operator+(Bruch const& a, Bruch const& b) { Bruch ergebnis = a; ergebnis += b; return ergebnis; }
(du stellst fest, dass ich im Prinzip einfach die Memberfunktion addieren zum operator+= und die freie Funktion addiere zum operator+ umfunktioniert habe)
Ähnliches macht man für den operator<< um Brüche genauso wie alles andere ausgeben zu können, also cout << c << endl; statt ausgeben(c).
- public-Variablen. Ein Bruch hat eine Invariante (eine Bedingung die für ein gültiges Objekt immer zutreffen muss), nämlich dass der Nenner nicht 0 ist. Ich würde auch noch hinzufügen, dass der Bruch immer gekürzt ist. Wenn du mit public-Variablen den direkten Zugriff auf Zähler und Nenner zuläßt, kannst du die Invariante nicht aufrechterhalten.
-
Kritikpunkte:
- public-Variablen. Ein Bruch hat eine Invariante (eine Bedingung die für ein gültiges Objekt immer zutreffen muss), nämlich dass der Nenner nicht 0 ist. Ich würde auch noch hinzufügen, dass der Bruch immer gekürzt ist. Wenn du mit public-Variablen den direkten Zugriff auf Zähler und Nenner zuläßt, kannst du die Invariante nicht aufrechterhalten.
Schlussfolgerung: Zaehler und Nenner sollten private sein ("Kapselung")
Ja, das stimmt. Ich hatte auch versucht, mit Hilfe einer get-Methode zu arbeiten, aber habs irgendwie nicht hinbekommen, darum hab ich den direkten Zugriff erst mal gelassen.
- zuviele Variablen. Ein Bruch hat *einen* Zähler und *einen* Nenner, nicht jeweils 3.
Stimmt natürlich auch. Aber irgendwie dachte ich, ich bräuchte für die Rechnungen drei Brüche, daher kommen wohl die ganzen Variablen.
- einlesen und ausgeben gehören nicht in die Klasse
hm, kann man sie denn nicht als Methoden ansehen?
- hier bietet sich Operatorüberladung an, aber es kann natürlich gut sein, dass du dich damit noch nicht beschäftigt hast.
Damit hast du Recht, aber ich kanns mir ja mal als Nächstes anschauen...
Ich werd mir jetzt in aller Ruhe mal deinen Vorschlag ansehen und versuchen ihn nachzuvollziehen...
Vielen Dank schon mal!
- public-Variablen. Ein Bruch hat eine Invariante (eine Bedingung die für ein gültiges Objekt immer zutreffen muss), nämlich dass der Nenner nicht 0 ist. Ich würde auch noch hinzufügen, dass der Bruch immer gekürzt ist. Wenn du mit public-Variablen den direkten Zugriff auf Zähler und Nenner zuläßt, kannst du die Invariante nicht aufrechterhalten.
-
Ich glaueb, du hast da was falsch verstanden...
Bei einer Klasse geht es immer um ein Objekt, also auch ein Bruch. Wenn deine Klasse Bruch heißt, dann stellt sie EINEN Bruch dar, und nicht wie du es erst gemacht hast, 2 STück, die man dann addieren kann usw. Dies macht man dann so wie Bashar, Für jeden Bruch ein eigenes Objekt erstellen und die Brüche dann mit Memberfunktionen des einen Bruches addieren, und den anderen als Argument übergeben.
Gruß, Maxi