Erstes OOP Projekt - Erschaffung einer Instanz namens Person - gelöst



  • @silent_max

    1. Was ist diner Meinung nach zwischen diesen beiden Zeilen anders, so dass deine IDE das eine Unterkringelt, das andere Nicht?

    cin  >> Passant.getHaarfarbe();
      cin  >> Passant.getGroeße;
    

    was meinst du, was

    string getHaarfarbe()
    {
       return Haarfarbe;
    };
    

    macht?
    Also jetzt mal so ganz genau. Nicht nur so in etwa. Und dann erkläre mir, warum

    cin  >> Passant.getHaarfarbe();
    

    funktioniert



  • otze schrieb:

    @silent_max

    1. Was ist diner Meinung nach zwischen diesen beiden Zeilen anders, so dass deine IDE das eine Unterkringelt, das andere Nicht?

    cin  >> Passant.getHaarfarbe();
      cin  >> Passant.getGroeße;
    

    Ja die beiden Klammern fehlen. Sorry, habe ich nicht (mehr) gesehen. Das blöde ist nur, dass der >> Operand immer noch rot unterringelt ist.

    otze schrieb:

    was meinst du, was

    string getHaarfarbe()
    {
       return Haarfarbe;
    };
    

    macht?
    Also jetzt mal so ganz genau. Nicht nur so in etwa.

    Die als string deklarierte Methode gibt einen string zurück würde ich jetzt mal sagen.

    otze schrieb:

    Und dann erkläre mir, warum

    cin  >> Passant.getHaarfarbe();
    

    funktioniert

    Da hast Du mich auf dem falschen Fuß erwischt, ich kann es nicht erklären.



  • Da musst Du Dich nicht wundern, es funktioniert nämlich nicht ...



  • Es funktioniert nicht, das stimmt, aber es kompiliert. Und warum kompiliert es?



  • Mit dem Getter erhälst du eine temporäre Kopie des String-Objekts (d.h. des Attributs), wo der aus dem Stream extrahierte Wert landet. Da aber das Objekt und damit auch die Eingabe gleich danach zerstört werden, ändert sich innerhalb deines Objektes nichts. Max, lies doch bitte was über Setter nach. ⚠



  • [Rewind] schrieb:

    Da aber das Objekt und damit auch die Eingabe gleich danach zerstört werden, ändert sich innerhalb deines Objektes nichts.

    Nein, es ändert sich innerhalb des Objekts nichts, weil das Innere des Objekts überhaupt nicht (verändernd) angesprochen wird.



  • freakC++ schrieb:

    Aber nur, wenn er keine Ahnung von Software Engineering hat.

    Argh. Ich rede nun ja wirklich nicht gerne Off-Topic, aber das ist eine ziemlich schlechte Verallgemeinerung (die eigentlich nur von jemandem kommen sollte, der keine Ahnung von Software Engineering hat :P). 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.

    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?)



  • 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!

    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?



  • 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?)

    Ist es besser, einfach unkontrollierten Zugriff auf die Daten zu erlauben? Schon sehr viele Probleme damit in altem Code gesehen, und man konnte nicht mal einen Breakpoint setzen. Außerdem verbirgt ein Setter Implementierungsdetails und ist einem public Member fast immer vorzuziehen, außer es geht vielleicht um extrem performancekritische Fälle.



  • 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.

    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?

    http://en.wikipedia.org/wiki/Dependency_injection



  • 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?

    http://en.wikipedia.org/wiki/Dependency_injection

    Verstehe ich nicht. Ist das eine Java-Technik? Ist sie in C++ relevant?



  • 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?

    http://en.wikipedia.org/wiki/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


Anmelden zum Antworten