Erstes OOP Projekt - Erschaffung einer Instanz namens Person - gelöst
-
TravisG schrieb:
volkard schrieb:
TravisG schrieb:
Für public-Attribute gibt es manchmal sehr gute Gründe die keine wirklichen Nachteile mit sich ziehen wenn man sie gut anwendet. Und nicht auf dem Level von "goto hat auch gute Anwendungsfälle" sondern schon wirklich gute.
Beispiel bitte!
Generell kann man sagen, dass es einige Anwendungsfälle gibt bei denen sich die Zugriffssemantik der dahinterliegenden Daten niemals ändert (der eigentlich Sinn von Getter-Methoden: man kann die Semantik des Datenzugriffs mit minimalen Codeänderungen umbiegen). Ein einfaches Beispiel, dass wahrscheinlich jedem mal über den Weg läuft wenn er mit GUIs oder Grafikprogrammierung zu tun hat:
struct Vector2 //oder: struct Point { float x,y; }Getter würden hier einfach keinerlei Vorteile bieten. Public hat dagegen einfach den Vorteil, dass es Code schrumpfen lässt und die Finger deutlich weniger beansprucht. Vergleiche:
float dot(const Vector2 &a, const Vector2 &b) { return a.x * b.x + a.y * b.y; }float dot(const Vector2 &a, const Vector2 &b) { return a.getX() * b.getX() + a.getY() * b.getY(); }Selbst bei diesem trivialen Beispiel erzeugen die zusätzlichen Klammern für mich eine kleine Unterbrechung des Leseflusses. Vielleicht geht es dir nicht so, aber da es mir so geht solltest du davon ausgehen, dass es auch noch andere gibt denen es ähnlich geht. Und das ist ein ziemlich einfaches Beispiel. In einer Codesektion, in der sehr viel auf die beiden Attribute zugegriffen wird, trägt das durchaus dazu bei die Übersicht zu verhindern (ich habe mit Java-Codebasen gearbeitet, wo hier wirklich getX() und getY() verwendet wurde, und das hat gewisse Codesektionen einfach massiv aufgebläht).
Warum also die längere Variante nehmen, wenn sie einfach keine Vorteile hat?
Generell sind "plain old data" Typen gute Kandidaten für Konstrukte mit öffentlichen Attributen.
Jo, da teile ich Deine Meinung.
-
volkard schrieb:
TravisG schrieb:
Volkard schrieb:
TravisG schrieb:
Außerdem sind viele Setter (wenn sie was anderes machen als Dependency-Injection) ein Zeichen von schlechtem Klassendesign (wozu braucht die Außenwelt ständig Schreibzugriffe auf deine privaten Daten?)
Dependency-Injection?
Verstehe ich nicht. Ist das eine Java-Technik? Ist sie in C++ relevant?
Hmm... du verstehst es bestimmt, kennst es aber nicht unter dem Namen. Der Begriff ist mit Sicherheit ein Java-Buzzword, und ich verwende ihn nun auch da die meisten Neuprogrammierer das Konzept darunter kennelernen
.Im Prinzip gehts einfach darum bestimmte Aufgaben in einer Softwarekomponente austauschbar zu machen (dadurch löst man harte Abhängigkeiten auf). Z.B. könntest du dir eine Klasse vorstellen, die einen Datensatz an Personen verwaltet und dir anbietet, nach Personen per Namen zu suchen. Dazu muss die Implementierung zwangsweise irgendeinen Suchalgorithmus benutzen. Die Klasse PersonenVerwaltung hat dann intern nur einen Zeiger auf eine abstrakte Basisklasse mit dem Namen "SpeicherUndSortierAlgorithmus" hinter dem sich dann DoofSpeichernUndLinearSuchen, BinärBaumSpeicherUndBinärbaumSuche, etc. verbergen.
Die "Dependency-Injection" ist dann einfach die Methode setzeSortierUndSpeicherAlgorithmus(SpeicherUndSortierAlgorithmus *konkreteImplementation) mit der man die konkrete Implementierung (dependency) setzen (inject) kann.
Das ist eigentlich das klassische Beispiel für das Strategy-Pattern, aber dependency injection ist einfach so ein Regenschirmbegriff für alles, dass Abhängigkeiten austauschbar macht (z.B. Strategy-Pattern, oder Component-Pattern, etc.).
-
Der Begriff Dependency Injection (DI) bzw. Inverse of Control (IoC) wird z.B. auch in C# benutzt.
Es ermöglicht besonders, einzelne Klassen und Methoden besser testen zu können, indem Abhängigkeiten zu externen Klassen (z.B. externe Ressourcenzugriffe, Logger, ...) durch Schnittstellen ersetzt werden, so daß diese durch (dummy) Mock-Implemenationen ersetzt werden (d.h. es wird nur die Funktionalität der eigentlich zu testenden Methode betrachtet).
Beim Test Driven Design (TDD) ist damit DI quasi fest vorgegeben.Dies Begriffe sollten also jedem Informatiker bekannt sein...
-
Th69 schrieb:
Dies Begriffe sollten also jedem Informatiker bekannt sein...
JA ! Dann bin ich ja doch nicht ganz doof

-
Th69 schrieb:
Der Begriff Dependency Injection (DI) bzw. Inverse of Control (IoC) wird z.B. auch in C# benutzt.
Sorry, aber C# ist Java.
-
Th69 schrieb:
Beim Test Driven Design (TDD) ist damit DI quasi fest vorgegeben.
Das sehe ich in C++ nicht.
-
Naja, in Java muss man das Strategy Pattern auch per Interface und Polymorphie implementieren. In C++ geht das ganze über Templates, damit ist es schneller, typsicherer und hat zur Laufzeit keine ungeplanten Nebeneffekte.
Nachteil ist damit aber die Nicht-Substituierbarkeit zur Laufzeit
-
Th69 schrieb:
Der Begriff Dependency Injection (DI) bzw. Inverse of Control (IoC) wird z.B. auch in C# benutzt.
Es ermöglicht besonders, einzelne Klassen und Methoden besser testen zu können, indem Abhängigkeiten zu externen Klassen (z.B. externe Ressourcenzugriffe, Logger, ...) durch Schnittstellen ersetzt werden, so daß diese durch (dummy) Mock-Implemenationen ersetzt werden (d.h. es wird nur die Funktionalität der eigentlich zu testenden Methode betrachtet).
Beim Test Driven Design (TDD) ist damit DI quasi fest vorgegeben.Machst du das wirklich so aufwändig? Für jeden Kram Mocks schreiben, damit du nur die Funktionalität der Klasse bzw. ein paar zusammengehörender Kassen einzeln testen kannst? Machst du dann auch noch Tests damit du weißt, ob die Klassen auch wirklich mit den anderen nichtmehr gemockten Klassen funktionieren, so wie sie im Programm dann verwendet werden?
-
Erst einmal vielen Dank für das rege Interesse und den vielen Antworten.
Ich bin dem Ziel bisschen näher gekommen.
In der Header-Datei "Klasse Person.h" habe ich folgende Deklaration gemacht:
#pragma once #include <string> using namespace std; class Person { private: string Vorname; string Nachname; int Alter; public: Person(); ~Person(); string getVorname(); string getNachname(); int getAlter(); void setVorname(string); void setNachname(string); void setAlter(int); };In "Klasse Person.cpp" habe ich folgende Implementierung geschrieben:
#include <iostream> #include "Klasse Person.h" using namespace std; Person::Person(void) { } Person::~Person() { } string Person::getVorname() { return Vorname; } string Person::getNachname() { return Nachname; } int Person::getAlter() { return Alter; } void Person::setVorname(string vn) { Vorname = vn; } void Person::setNachname(string nn) { Nachname = nn; } void Person::setAlter(int age) { Alter = age; }In "Person APP.cpp" habe ich folgenden Code geschrieben:
#include <iostream> #include "Klasse Person.h" using namespace std; #define MAX_ANZAHL 2 void einePerson() { string str; Person Passant; cout << "Wie ist ihr Vorname?\n"; cin >> str; Passant.setVorname(str); cout << "Wie ist Ihr Nachname?}n"; cin >> str; Passant.setNachname(str); cout << "Wie alt sind Sie?\n"; int age; cin >> age; Passant.setAlter(age); cout << "\n"; cout << "Das haben Sie eingegeben:\n"; cout << "\n"; cout << "Vorname: " << Passant.getVorname() << "\n"; cout << "Nachname: " << Passant.getNachname() << "\n"; cout << "Alter: " << Passant.getAlter() << "\n"; } void mehrerePersonen() { Person Personen[MAX_ANZAHL]; for(int i=0; i<MAX_ANZAHL; i++) { string str; cout << "Wie ist ihr Vorname?\n"; cin >> str; Personen[i].setVorname(str); cout << "Wie ist Ihr Nachname?\n"; cin >> str; Personen[i].setNachname(str); cout << "Wie alt sind Sie?\n"; int age; cin >> age; Personen[i].setAlter(age); } } int main() { //einePerson(); mehrerePersonen(); return 0; }Jetzt hätte ich nur ein Anliegen...
Was ist
#pragma once?? Was bedeutet der Befehl?
Gruß
Max
-
Sagt dem Compiler, dass der Header nicht nochmal eingebunden wird. Das ist interessant sobald eine Quelldatei 2 Header einbindet, die beide einen dritten Header einbinden. (Der dann doppelt eingebunden ist, der Compiler streikt dann nämlich)
Es gibt 2 Optionen:
Include-Guards (portabel):
#ifndef _FOO_HPP_INCLUDED_ // Oder irgendein anderer, einzigartiger Name. #define _FOO_HPP_INCLUDED_ .... Code ... #endif // _FOO_HPP_INCLUDED_oder eben per Pragma (eher unportabel)
#pragma once .... Code ...Jede Headerdatei sollte eines von beiden machen. (Ja, nicht ganz jede, aber praktisch jede)
-
Denn nehmen wir mal an, du hast folgende Situation:
// header1.h void foo(); // header2.h #include "header1.h" void bar(); // main.cpp #include "header1.h" #include "header2.h"Nach dem Einbinden der Header, sieht es in main.cpp so aus:
void foo(); // aus header1.h void foo(); // aus header1.h in header2.h void bar(); // aus header2.hDer Linker beschwert sich nun, dass die Funktion foo() zweimal deklariert wurde.
Mit #pragma once oder include-Gurads wird jeder Header nur einmal eingebunden, das sieht dann so aus:void foo(); // aus header1.h // header1.h aus header2.h wird nicht eingebunden, weil header1.h bereits inkludiert wurde void bar(); // aus header2.hEdit: Nochmal etwas zum Stil: kein using namespace std in Headern, schreibe stattdessen std::string!
-
Nathan schrieb:
Edit: Nochmal etwas zum Stil: kein using namespace std in Headern, schreibe stattdessen std::string!
Warum kein
using namespace std;verwenden??
Ist denn der Code bisher sonst in Ordnung??
-
silent_max schrieb:
Nathan schrieb:
Edit: Nochmal etwas zum Stil: kein using namespace std in Headern, schreibe stattdessen std::string!
Warum kein
using namespace std;verwenden??
Ist denn der Code bisher sonst in Ordnung??
Weil du dir vorstellen kannst, dass der Inhalt jeder Headerdatei, die per include eingebunden wird, direkt in die Datei reinkopiert wird, die sie einbindet. Macht ein Header using namespace xxxx, dann gilt das in jeder Headerdatei die danach eingebunden wird und in jeder Sourcedatei - es hebelt also komplett den Zweck von Namensräumen aus.
-
Hallo liebe Gemeinde.
Euch wird das Thema wahrscheinlich schon zum Hals raus hängen, aber ich verstehe es immer noch nicht ganz.
Ich wollte dem ganzen Code noch eine Abfrage nach der Postleitzahl hinzufügen.
Also in "Klasse Person.h" habe ich geschrieben:
class Person { private: int Postleitzahl; public: int getPostleitzahl; void setPostleitzahl(int); }In "Klasse Person.cpp" habe ich geschrieben:
int Person::getPostleitzahl() { return Postleitzahl; } void Person::setPostleitzahl(int postcode) { Postleitzahl = postcode; }Hier bekomme ich die Fehlermeldung, dass bei
getPostleitzahl()Error: Die Deklaration ist nicht mit "int Person::getPostleitzahl" kompatibelIn "Person APP.cpp" habe ich geschrieben:
cout << "Wo wohnen Sie? Geben Sie die Postleitzahl an!\n"; int Postleitzahl; cin >> Postleitzahl; Personen[i].setPostleitzahl(postcode)Hier bekomme ich folgende Fehlermeldung:
Error: "class "Person"" hat keinen Member "setPostleitzahl" Error: Der Bezeichner ""postcode"" ist nicht definiertWo ist mein Denkfehler??
Gruß
Max
-
Klammern bei Deklaration vergessen.
Und diverse Semikolons.
-
Also ich habe die vermeintlichen (Leichtsinns-) Fehler ausgebessert, dennoch kommen in der "Person APP.cpp" die Meldungen
Error: "class "Person"" hat keinen Member "setPostleitzahl" Error: Der Bezeichner ""postcode"" ist nicht definiertIch sehe es echt nicht, wo der Fehler ist...
-
silent_max schrieb:
Also ich habe die vermeintlichen (Leichtsinns-) Fehler ausgebessert, dennoch kommen in der "Person APP.cpp" die Meldungen
Error: "class "Person"" hat keinen Member "setPostleitzahl" Error: Der Bezeichner ""postcode"" ist nicht definiertIch sehe es echt nicht, wo der Fehler ist...
Ohne Quellcode sehe ich hier auch nichts

-
Also hier mal der gesamte Quellcode:
In "Klasse Person.h"
#pragma once #include <string> using namespace std; class Person { private: string Vorname; string Nachname; int Alter; int Postleitzahl; public: Person(); ~Person(); string Vorname(); string Nachname(); int getAlter(); int Postleitzahl(); void setVorname(string); void setNachname(string); void setAlter(int); void setPostleitzahl(int); };In "Klasse Person.cpp":
#include <iostream> #include "Klasse Person.h" using namespace std; Person(void) { } ~Person() { } string Person::getVorname() { return Vorname; } string Person::getNachname() { return Nachname; } int Person::getAlter() { return Alter; } int Person::getPostleitzahl() { return Postleitzahl; } void Person::setVorname(string vn) { Vorname = vn; } void Person::setNachname(string nn) { Nachname = nn; } void Person::setAlter(int age) { Alter = age; } void Person::setPostleitzahl(int postcode) { Postleitzahl = postcode; }In "Person APP.cpp" habe ich geschrieben:
#include <iosteram> #include "Klasse Person.h" using namespace std; #define MAX_ANZAHL 2 void einePerson() { string str; Person Passant; cout << "Wie ist Ihr Vorname?\n"; cin >> str; Passant.setNachname(str); cout << "Wie ist Ihr Nachname?\n"; cin >> str; Passant.setNachname(str); cout << "Wie alt sind Sie?\n" int age; cin >> age; Passant.setAlter(age); cout << "\n"; cout << "Das haben Sie eingegeben: \n"; cout << "\n"; cout << "Vorname: " << Passant.getVorname() << "\n"; cout << "Nachname: " << Passant.getNachname() << "\n"; cout << "Alter: " << Passant.getAlter() << "\n"; } void mehrerePersonen() { Person Personen[MAX_ANZAHL]; for (int i = 0; i < MAX_ANZAHL; i++) { string str; cout << "Wie ist Ihr Vorname?\n"; cin >> str; Personen[i].setNachname(str); cout << "Wie ist Ihr Nachname?\n"; cin >> str; Personen[i].setNachname(str); cout << "Wie alt sind Sie?\n"; int age; cin >> age; Personen[i].setAlter(age); cout << "Wo wohnen Sie? Geben Sie Ihre Postleitzahl an!\n"; int Postleitzahl; cin >> Postleizahl; Personen[i].setPostleitzahl(postcode); } } int main() { //einePerson(); mehrerePersonen(); return 0; }Ich weiß echt nicht, wo der Fehler sein kann...
-
puh also mein Compiler spuckt mit 20 Fehler oder so aus. Es fehlen ein paar get-Funktionen. Ich weiß auch nicht, ober Header ein Leerzeichen enhalten dürfen? Konstruktor und Destruktor wurden nicht definiert. Bei getAlter ist ein hast du "get Alter" geschrieben, mit dem Leerzeichen.

-
[quote="silent_max"
int Postleitzahl; cin >> Postleizahl; Personen[i].setPostleitzahl(postcode);Ich weiß echt nicht, wo der Fehler sein kann...[/quote]
In diesen drei Zeilen.