Frage bzgl getter-Methode



  • Hallo,
    ich habe folgenden Quelltext:

    main Klasse

    #include <iostream>
    #include <vector>
    #include "Test.h"
    
    using namespace std;
    
    int main() {
    
    	Test t;
    	t.getVector().push_back(50);
    	return 0;
    }
    

    Test klasse

    #include <iostream>
    #include <vector>
    #include "Test.h"
    
    using namespace std;
    
    Test::Test() {
    
    }
    
    Test::~Test() {
    
    }
    
    vector<int> Test::getParticipants() {
    	return participants;
    }
    
    void Test::setParticipants(vector<int> *pparticipants) {
    	participants=*pparticipants;
    }
    

    meine frage lautet nun, warum die methode

    t.getVector().push_back(50);
    

    nicht dem vector des objekts t den wert 50 zufügt. wenn ich den vector nach aufruf der push_back methode nach der Größe frage bleibt diese bei 0.

    vermutlich merkt mans eh, aber zu meiner entschuldigung: ich komme aus dem Java bereich 😃

    hier würde mich dann auch noch interessieren wie man grundsätzlich getter und setter gestalten sollte. immer einen pointer übergeben?

    viele dank für alle hilfe 🙂



  • Weil du eine Kopie des Objekts zurückgibst. Du musst eine Referenz zurückgeben:

    vector<int>& Test::getParticipants() {
        return participants;
    }
    

    grüße



  • David_pb schrieb:

    Weil du eine Kopie des Objekts zurückgibst. Du musst eine Referenz zurückgeben:

    vector<int>& Test::getParticipants() {
        return participants;
    }
    

    grüße

    Das ist noch viel schlimmer, als ein neues Objekt zurückzugeben :p

    Der Rückgabetyp muss const sein.



  • test-mensch schrieb:

    hier würde mich dann auch noch interessieren wie man grundsätzlich getter und setter gestalten sollte. immer einen pointer übergeben?

    Eine Referenz auf ein konstantes Objekt.



  • Michael E. schrieb:

    David_pb schrieb:

    Weil du eine Kopie des Objekts zurückgibst. Du musst eine Referenz zurückgeben:

    vector<int>& Test::getParticipants() {
        return participants;
    }
    

    grüße

    Das ist noch viel schlimmer, als ein neues Objekt zurückzugeben :p

    Der Rückgabetyp muss const sein.

    Räusper... ja sicher doch! 😃 Dann kann er auch besonders gut auf nicht konstante Methoden von vector zugreifen...

    vector<int>& Test::getParticipants() {
        return participants;
    }
    
    const vector<int>& Test::getParticipants() const {
        return participants;
    }
    

    So hat er ne Absicherung von wegen konstantem Objekt und das Abändern des Vektors klappt auch bei nicht konstanten Objekten.

    grüße



  • David_pb schrieb:

    So hat er ne Absicherung von wegen konstantem Objekt und das Abändern des Vektors klappt auch bei nicht konstanten Objekten.

    Ja, wobei test::add_paricipant(int) in den meisten Fällen wahrscheinlich das Geschickteste sein wird.



  • Durchaus, aber das war nich die Anforderung die test-mensch stellte! 🙂



  • David_pb schrieb:

    Räusper... ja sicher doch! 😃 Dann kann er auch besonders gut auf nicht konstante Methoden von vector zugreifen...

    Soll er ja auch gar nicht. Eine get-Methode gibt dir was zu lesen. Wenn du schreibenden Zugriff erlaubst, ist deine ganze Datenkapselung futsch.

    vector<int>& Test::getParticipants() {
        return participants;
    }
    
    const vector<int>& Test::getParticipants() const {
        return participants;
    }
    

    So hat er ne Absicherung von wegen konstantem Objekt und das Abändern des Vektors klappt auch bei nicht konstanten Objekten.

    Wo liegt denn der Sinn darin, dem Benutzer meiner Klasse vollen Zugriff auf die Interna zu geben?



  • Michael E. schrieb:

    Soll er ja auch gar nicht. Eine get-Methode gibt dir was zu lesen. Wenn du schreibenden Zugriff erlaubst, ist deine ganze Datenkapselung futsch.

    Wie begründest Du das?

    Abgesehen finde ich diese ganze Hyper-Kapselung à la Java in C++ fehl am Platz, die bläht nur den Code auf und ist damit eher kontraproduktiv.

    /EDIT: Der letzte Satz ergibt im aktuellen Kontext keinen Sinn.



  • David_pb schrieb:

    Durchaus, aber das war nich die Anforderung die test-mensch stellte! 🙂

    Dann sollte sich test-mensch mit dieser Alternative lieber abfinden 😃

    Denn er manipuliert anscheinend von AUßEN ein privates Mitglied der Klasse ⚠ ⚠ ⚠

    Da macht es natürlich keine Sinn den Vector zurückzuliefern und ihn dann zu manipulieren...

    Und dem was Michael E. meint stimme ich auch zu, es macht keinen Sinn ( bzw. ist unsauber, nicht C++'isch 😉 ) ein Objekt über ne get-Methode zu manipulieren ...



  • Konrad Rudolph schrieb:

    Michael E. schrieb:

    Soll er ja auch gar nicht. Eine get-Methode gibt dir was zu lesen. Wenn du schreibenden Zugriff erlaubst, ist deine ganze Datenkapselung futsch.

    Wie begründest Du das?

    Ein Grundsatz der Datenkapselung ist, dass man keine gekapselten Datenfelder öffentlich macht. Der Sinn besteht darin, den Zugriff auf diese genau kontrollieren und, wenn nötig, konsistent abändern zu können. Nun braucht man aber auch oft Zugriff auf diese Member, deshalb gibts dann Getter zum (kontrollierten) Lesen und Setter zum (kontrollierten) Schreiben.

    [Edit] Diese Funktionen müssen und sollen vor allem bei komplexen Datentypen IMHO meistens nicht stupide den Parameter dem Member zuweisen bzw. den Member zurückliefern, sondern man sollte sich genau überlegen, welche Operationen unterstützt werden sollen, und für diese ein Interface geben. [/Edit]

    Wenn du jetzt über den Getter vollen Schreibzugriff implementierst, geht das erst mal völlig am Funktionsnamen und -zweck vorbei und zweitens hast du die Kontrolle über den Member verloren, denn der Benutzer der Klasse kann tun und lassen, was er will.

    Abgesehen finde ich diese ganze Hyper-Kapselung à la Java in C++ fehl am Platz, die bläht nur den Code auf und ist damit eher kontraproduktiv.

    Das sagst du bei diesem Beispiel?

    /EDIT: Der letzte Satz ergibt im aktuellen Kontext keinen Sinn.

    Begründung?



  • Michael E. schrieb:

    Konrad Rudolph schrieb:

    Michael E. schrieb:

    Soll er ja auch gar nicht. Eine get-Methode gibt dir was zu lesen. Wenn du schreibenden Zugriff erlaubst, ist deine ganze Datenkapselung futsch.

    Wie begründest Du das?

    Ein Grundsatz der Datenkapselung ist, dass man keine gekapselten Datenfelder öffentlich macht.

    Doch, man macht sie eben durch die Kapselung öffentlich -- allerdings (durch die Kapselung eben) kontrolliert. Es kann durchaus sinnvoll sein, über diese Kapselung auch Daten zu modifizieren, nicht nur zu lesen. Gerade bei Feldern ist das sinnvoll; eine eigene Methode zum Hinzufügen, Entfernen etc. von Daten zu schreiben ist redundant und IMHO falsch designed.

    Wenn du jetzt über den Getter vollen Schreibzugriff implementierst, geht das erst mal völlig am Funktionsnamen und -zweck vorbei

    Zu hast recht, Schreibzugriff sollte über einen Setter erfolgen -- modifizierender Zugriff aber IMHO nicht unbedingt. Leider kann man das in C++ AFAIK nicht (oder nur sehr aufwendig) trennen. Daher muss man wohl in den sauren Apfel beißen und sich für eine der Varianten entscheiden. Und das komplette Interface nachzuimplementieren halte ich für den falschen Ansatz.

    Abgesehen finde ich diese ganze Hyper-Kapselung à la Java in C++ fehl am Platz, die bläht nur den Code auf und ist damit eher kontraproduktiv.

    Das sagst du bei diesem Beispiel?

    Nein, sage ich eben nicht: Ich habe doch hinzugefügt, dass dieser Satz hier nicht passt. Ich hätte ihn auch entfernen können aber ich benutze Edits grundsätzlich nie zum Löschen in Postings.



  • Konrad Rudolph schrieb:

    Gerade bei Feldern ist das sinnvoll; eine eigene Methode zum Hinzufügen, Entfernen etc. von Daten zu schreiben ist redundant und IMHO falsch designed.

    Hmmmm, das leuchtet ein. Ist eigentlich logisch bei Vectoren und Konsorten, wenn man drüber nachdenkt ...
    Da hast du Recht 😉



  • ähm, also eure diskussion kann ich ja halbwegs nachvollziehen, dennoch bleiben fragen.

    wie schaut den nun eine einfach getter/setter methode aus? mal vorausgesetzt, ich wähle den meines erachtens nach auch besseren weg, dass ich sicherstelle, dass getter nur zum lesen gebraucht werden ( auch wenn sich das jetzt mit meiner ausgangssituation beißt 😉 ).

    ich möchte also eine const referenz zurückbekommen, richtig?

    wenn ich das nun so mache, wobei value ein einfacher int sei:

    const int &Test::getValue() const {
     return value;
    }
    

    bekomme ich ja eine referenz zurück.

    sollte ich nun einmal in die verlegenheit kommen, und mit dem rückgabewert weiterarbeiten wollen und ihn hierfür temporär in einer variablen speichern wollen, ist dann die referenz die ich zurückbekomme schon mein pointer (soweit ich weiß sind referenzen ja pointer die sich wie variable verhalten) oder muss ich dafür sorgen dass ich irgendwie

    Test t;
    int *tmp = t.getTest();
    

    ereiche?

    was bei mir nicht funktioniert ich aber eigentlich logsich fände, scließlich funktioniert ja auch

    int tmp = 5;
    int *ptmp = &tmp;
    

    nicht wahr? 😉

    zu später stunde bin ich schon ein bißchen malle in der birne, bitte seht mir also nach falls das alles nonsens ist 😃

    dankbar wär ich mal fpr eine einfache getter und setter methode mit meinetwegen einem einfachen int o.ä.

    soweit ihr h0x3r :p

    gut n8



  • Michael E. schrieb:

    Ein Grundsatz der Datenkapselung ...

    Das gilt aber nur solange es sich tatsächlich um Interna handelt. Und deine Behauptung

    Michael E. schrieb:

    Eine get-Methode gibt dir was zu lesen

    kommt irgendwie auch nicht ganz hin; eine get-Methode gibt dir was, Punkt.



  • Konrad Rudolph schrieb:

    Doch, man macht sie eben durch die Kapselung öffentlich -- allerdings (durch die Kapselung eben) kontrolliert. Es kann durchaus sinnvoll sein, über diese Kapselung auch Daten zu modifizieren, nicht nur zu lesen.

    wtf?

    Konrad Rudolph schrieb:

    Gerade bei Feldern ist das sinnvoll; eine eigene Methode zum Hinzufügen, Entfernen etc. von Daten zu schreiben ist redundant und IMHO falsch designed.

    class workshop
    {
    public:
      workshop( std::size_t max_participants, std::size_t min_age );
      // Kapselung "oeffentlich machen" aka "aushebeln":
      std::vector<person> get_participants();
    };
    
    int main()
    {
      workshop w(20, 16);
      vector<person> p = w.get_participants();
      generate_n(back_inserter(p), 1000000, generate_random_person);
    }
    

    Konrad Rudolph schrieb:

    Zu hast recht, Schreibzugriff sollte über einen Setter erfolgen -- modifizierender Zugriff aber IMHO nicht unbedingt. Leider kann man das in C++ AFAIK nicht (oder nur sehr aufwendig) trennen. Daher muss man wohl in den sauren Apfel beißen und sich für eine der Varianten entscheiden. Und das komplette Interface nachzuimplementieren halte ich für den falschen Ansatz.

    Äh, ja. Was ist denn der Unterschied zwischen Schreibzugriff und modifizierendem Zugriff?



  • test-mensch schrieb:

    sollte ich nun einmal in die verlegenheit kommen, und mit dem rückgabewert weiterarbeiten wollen und ihn hierfür temporär in einer variablen speichern wollen, ist dann die referenz die ich zurückbekomme schon mein pointer (soweit ich weiß sind referenzen ja pointer die sich wie variable verhalten) oder muss ich dafür sorgen dass ich irgendwie

    Test t;
    int *tmp = t.getTest();
    

    ereiche?

    Nein, die Referenz die du zurückbekommst ist schon deine Referenz, nicht dein Pointer:

    Test t;
    int& tmp = t.getTest();
    


  • finix schrieb:

    class workshop
    {
    public:
      workshop( std::size_t max_participants, std::size_t min_age );
      // Kapselung "oeffentlich machen" aka "aushebeln":
      std::vector<person> get_participants();
    };
    
    int main()
    {
      workshop w(20, 16);
      vector<person> p = w.get_participants();
      generate_n(back_inserter(p), 1000000, generate_random_person);
    }
    

    Was soll mir der Code sagen?

    Konrad Rudolph schrieb:

    Zu hast recht, Schreibzugriff sollte über einen Setter erfolgen -- modifizierender Zugriff aber IMHO nicht unbedingt. Leider kann man das in C++ AFAIK nicht (oder nur sehr aufwendig) trennen. Daher muss man wohl in den sauren Apfel beißen und sich für eine der Varianten entscheiden. Und das komplette Interface nachzuimplementieren halte ich für den falschen Ansatz.

    Äh, ja. Was ist denn der Unterschied zwischen Schreibzugriff und modifizierendem Zugriff?

    Schreibzugriff ist eine Zuweisung.

    vector<T> x;
    x = vector<T>(); // Schreibzugriff, sollte über 'set'-Methode gehen.
    x.push_back(T()); // Modifizierender Zugriff, sollte über 'get'-Methode gehen.
    


  • Hi,

    ich muß gestehen, dass ich erst überhaupt nicht Michael E.'s Meinung zur Datenkapselung teilte. Ich hatte schon ein "Fenster für den Widerspruch" geöffnet ... und nun denke ich doch, dass er (im Grunsatz) Recht hat:
    Wenn die kapselnde Klasse Test einen vector besitzt, muß sie auch die alleinige Verantwortung über seinen Inhalt haben.
    Das Problem bei dem nonconst-Ref-getter ist, dass sie aber keinerlei Möglichkeiten hat, diese Konsistenz zu gewährleisten.

    Gleichzeitig stimmt aber auch, dass ein "Durchreichen" der einzelnen vector-Methoden aufgebläht, fehleranfällig, verwirrend, .... einfach schlecht ist.

    Deswegen mein Vorschlag (gar nichts Exotisches): Klassischer getter und setter !

    Und genau DAS hat test-Mensch im Klassendesign richtig gemacht !

    Aber er hat eben nicht den Setter aufgerufen !

    Statt

    t.getVector().push_back(50);
    

    hätte der Aufruf lauten müssen:

    vector<int> v = t.getVector(); // gib mir eine Kopie Deines Vektors
    v.push_back(50); // Mache alle Veränderungen, die Du willst
    t.setVector(v); // Prüfe, ob das für Dich konsistent ist und setze das dann intern
    

    Eigentlich lautet die Antwort auf seine Frage

    warum die methode...nicht dem vector des objekts t den wert 50 zufügt

    "Weil Du t nicht mitgeteilt hast, dass Du seinen Zustand ändern möchtest !"
    😃

    Gruß,

    Simon2.

    P.S.: Jaja, ich weiß: Jeder C++er zuckt zusammen, wenn ein vector kopiert wird, aber hier geht es erstmal um ein sinnvolles Design - muss man anschließend (wirklich) optimieren (weil es der Test und das Profiling ergeben haben - nicht, weil man es "schöner" fände), kann man immer noch diverse Wege gehen.

    • z.B. tatsächlich einzelne "spezielle Zugriffsmethoden" durchschleifen ...
    • oder ein "Konsistentflag" mit einer entsprechenden "check()-Funktion" einbauen ...
    • oder intern eine Kopie zu halten, die man nach außen gibt und erst beim "sync()-Call" konsistenz zu checken und intern auf den "echten Vektor" kopieren
    • ....

    P.P.S.: Hat sich bei den Vorschlägen der anderen mal jemand überlegt, was passiert, wenn es in Test sinnvoller wäre, die Daten anders zu speichern als in einem vector ? Immerhin ist eine Idee hinter Kaspelung auch die Trennung der Schnittstelle von der internen Datenhaltung...



  • Ich weiß nicht, ob das Beispiel zu wild, oder extrem ungewöhnlich ist, aber was ist mit dem w3c-DOM?
    Was ist mit getElementsByTagName?
    Sollte es verboten sein, die erhaltenen Elemente zu modifizieren?

    Konrad Rudolph schrieb:

    Zu hast recht, Schreibzugriff sollte über einen Setter erfolgen -- modifizierender Zugriff aber IMHO nicht unbedingt. Leider kann man das in C++ AFAIK nicht (oder nur sehr aufwendig) trennen.

    Doch gerade in C++ kann man das sauber trennen. Es sind Java, C#, ..., die alle im Gegensatz zu C++ keine const correctness unterstützen.


Log in to reply