Compiler findet Ctor nicht



  • Und weiter? Die angeführten Vorteile sind doch arg an den Haaren herbeigezogen. Wenn ich den Getter als simple Durchreichefunktion ausgebe, dann ist die Bindung an das Attribut so eng, dass ich da nicht einfach etwas ändern kann. Der Datentyp muss erhalten bleiben, erst recht bei einer Referenz. Und für die Offensichtlichkeit, da haben wir es schon wieder, sollte sich auch der Name nicht allzu weit entfernen. Member x in y umzubennen und weiterhin über getx() auf ihn zuzugreifen ergibt wenig Sinn. Und leere Getter aus Kompatibilitäts-Gründen bereit zu stellen, halte ich doch für arg fragwürdig. In der Regel bezweckt man mit der Anforderung eines Wertes etwas. Und damit das noch ordnungsgemäß funktioniert ist es gar nicht so verkehrt den Code an das neue Klassendesign anpassen zu müssen.



  • Es geht einfach um die Trennung von Interface und Implementation. Das ist DAS Grundlegende Konzept der Objektorientierung.

    Wenn ich den Getter als simple Durchreichefunktion ausgebe, dann ist die Bindung an das Attribut so eng, dass ich da nicht einfach etwas ändern kann.

    Du programmierst aber nicht nur für den Augenblick, sondern möchtest evtl. später noch etwas verändern. Dann ist der Getter plötzlich keine simple Durchreichefunktion mehr. Davon muss der Benutzer deiner Klasse aber nichts wissen, d.h. er ruft einfach immer noch getX() auf und damit hat es sich.
    Das geht mit öffentlichen Attributen nicht.

    Die angeführten Vorteile sind doch arg an den Haaren herbeigezogen.

    Nein, sie beruhen auf langer Erfahrung und Logik.



  • LesenBildet schrieb:

    Dann ist der Getter plötzlich keine simple Durchreichefunktion mehr.

    Das Argument könnte man zählen lassen. Nur war hier von Änderungen im Typ und Namen des Members die Rede und nicht vom Zugriff auf den Member. Das Problem wirst du mit der Methode nicht umgehen können. Du kannst natürlich dir x mit getY() holen, aber das ist doch kein schönes Design die Bezeichner bis zur völligen Verwirrung wiederzuverwenden, nur aus Legacy-Gründen?

    LesenBildet schrieb:

    Die angeführten Vorteile sind doch arg an den Haaren herbeigezogen.

    Nein, sie beruhen auf langer Erfahrung und Logik.

    Es geht mir nicht darum Getter als Unsinn abzuwerten. Nur kann ich keinen Mehrwert erkennen, wenn ich einfach nur ein return im Getter mach und eine non-const-Referenz zurück gebe. Und was das lesen bildet betrifft, in dem Artikel wird diese Ansicht geteilt. Es wird aber darauf hingewiesen und jetzt kommen wir zu deinem Punkt, dass man vielleicht später in den Getter noch Funktionalität einbauen möchte. Vor Anpassungen wegen eines geänderten Typen kann mich diese Vorgehensweise aber nicht bewahren, wahrscheinlich wäre da ein direkter Member-Zugriff sogar sinnvoller.



  • Der Punkt ist, dass der Aufrufer immer noch nicht weiß, wie mein internes Datenformat aussieht.

    Ich kann zum Beispiel sowas machen:

    class Foo{
    private: 
      double x,y,z;
    public:
        double& getX(){return x};
        double& getY(){return y};
        double& getZ(){return z};
    };
    

    Ich kann mir auch später sagen: ne, nen Vektor so darzustellen ist vielleicht doch nicht so gut. Vielleicht sollte ich intern doch was besseres verwenden.

    kein Problem:

    class Foo{
    private: 
      MyVector vec;
    public:
        double& getX(){return vec[0]};
        double& getY(){return vec[1]};
        double& getZ(){return vec[2]};
    };
    

    Point proved.

    //edit
    sowas mache ich übrigens die ganze Zeit. Ein Getter nach obigem Schema erlaubt Restriktionen des Zugriffs auf die internen Daten. So kann ich im Beispiel oben absolute Kontrolle über die Positionsdaten gewähren, ohne die Kontrolle über die Größe des Vektors (zum Beispiel) zu verlieren.



  • Und wenn du jetzt schon festgestellt hast, dass dein Foo nicht die einzelnen Komponenten des Vectors kennen muss, warum muss es dann wissen, wie man auf sie zugreift? Konsequenterweise müsste dein getX bis getZ in den Vector wandern und du den Vector zurück geben. Das würde zwar den Designvorteil, den du dir hier ermogeln wolltest vernichten, aber wäre dann echte Objektorientierung.



  • Und was das lesen bildet betrifft

    Bitte nicht zu ernst nehmen. 🤡
    Ich hatte mich so genannt, weil deine Fragen exakt so im Link beantwortet wurden - mehr nicht. Inzwischen nenne ich mich nur noch so, damit man erkennt, dass hier immer der gleiche antwortet.

    Konsequenterweise müsste dein getX bis getZ in den Vector wandern und du den Vector zurück geben.

    Nein, müsste es nicht, da es den Benutzer meiner Klasse gar nicht zu interessieren hat, in welcher Dateistruktur die Daten vorliegen. Es kann dem User doch egal sein, ob die einzelnen Koordinaten (um beim x-y-z Beispiel zu bleiben) in einem std::vector, std::array oder als einzelne Werte gespeichert sind.
    Und genau deswegen schreibt man solche "trivialen" Getter und Setter, damit man flexibel bleiben kann.



  • Nick Unbekannt schrieb:

    Das würde zwar den Designvorteil, den du dir hier ermogeln wolltest vernichten, aber wäre dann echte Objektorientierung.

    Wie kommst du darauf? Den Benutzer von Foo soll es ja eben nicht interessieren, wie die Klasse innen funktioniert. DAS ist Objektorientierung...



  • Dann ist das ein schlechtes Beispiel, weil es hier offensichtlich ist, dass es sich um ein zusammengehöriges Objekt handelt. Anders lässt sich auch nicht begründen, warum man es intern als einzelnes Objekt handhaben will. Warum sollte ich aber als Nutzer wenn ich die X-Komponente des Vektors haben will, nicht den Vektor anfordern? Erstens muss Foo so nicht wissen, wie ein Vektor ist und zweitens kann ich so weiter im Kontext des Vektors verfahren. Dazu muss ich auch nicht wissen, ob Vektor ein std::vector oder std::array ist, da wir da ja wieder bei der Kapselung angelangt sind.
    In der Praxis würde ich die alten Getter auch stehen lassen. Dass wäre aber ein Punkt, wo ich in Java deprecated über die Methode schreiben würde.



  • @otze: Das ist kein getter. Das ist ein getter und setter in einem und das finde ich sau mies.
    Was ist z.B., wenn du im Getter nur einen gecachten Wert liefern willst, aber im setter() Checks durchführen willst, die nur bei Erfolg in den Cache schreiben? Da klappt dein Ansatz schon mal nicht. Dann hättest du Klassen mit den üblichen getter/setter und ein paar Klassen mit diesen Hybrid getter/setter und kein konsistentes System mehr => hässlich.

    Abgesehen davon ist es einfach syntaktisch hässlich. Oder willst du mir sagen, dass du solche Konstrukte schön findest?

    foo.bar() = foo2.bar();
    foo.bar()+=foo2.bar();
    

    Igitt. 👎



  • Anmerker2 schrieb:

    Oder willst du mir sagen, dass du solche Konstrukte schön findest?

    Ja findet er.

    http://www.c-plusplus.net/forum/p1992298#1992298



  • Warum sollte ich aber als Nutzer wenn ich die X-Komponente des Vektors haben will, nicht den Vektor anfordern?

    Nochmal: Weil du gar nicht weißt, dass das Objekt intern ein Vektor speichert.

    Betrachte doch ein Objekt einfach mal wie eine Blackbox. Du weißt was du hineingeben kannst und was du herausbekommen kannst, aber nicht was innen drin passiert.
    Öffentliche Attribute verletzen dieses Blackbox-Prinzip, da sie Klasseninterna preisgeben.

    Du meinst, bei trivialen Gettern/Settern kann man auch öffentliche Attribute verwenden. Ich denke, das sollte man nicht machen, weil man sich so die Flexibiltät raubt, später die Implementation zu ändern.

    Ein weiteres, jedoch weniger wichtiges Argument, ist, dass man so ein konsistentes Interface erhält, da ja nicht alle Attribute einer Klasse zwingenderweise direkt in der Klasse direkt gespeichert sind, sondern evtl. aus anderen Werten errechnet werden müssen. Trotzdem greift man auf die gleiche Art und Weise auf diese "Attribute" zu.
    Kleines Beispiel:

    class Triangle 
    {
    public:
      point a(); // a könnte auch ein public member vom Triangle sein
      void a(point new_a); // " 
    
      double area(); // Geht hier nicht mehr, da die Fläche nicht direkt im 
      vec normal();  // zwischengespeichert wird
    
    private:
      point m_a, m_b, m_c;
    };
    


  • otze, I am disappoint



  • Nick Unbekannt schrieb:

    Und wenn du jetzt schon festgestellt hast, dass dein Foo nicht die einzelnen Komponenten des Vectors kennen muss

    Das sagst du, nicht ich. Ich kann die Vektordarstellung schöner finden, weil es mir meine Arbeit erleichtert. Vielleicht bringt MyVector einfach ganz viele tolle Operationen, die ich einfach brauche. Trotzdem kann die Klasse immer noch genau so die einzelnen Werte verwenden. Und in der Tat kennt Foo immer noch alle Werte, nur nicht ihre Implementation.

    Konsequenterweise müsste dein getX bis getZ in den Vector wandern und du den Vector zurück geben.

    Nö, warum. Das ist ein Implementationsdetail.Ich kann dir mal ein konkreteres Beispiel nennen: Quaternionen. Intern macht es unglaublich Sinn, den komplexen Teil als 3D-Vektor darzustellen, einfach weil sich das Quat-produkt auf Skalar und Kreuzprodukt abbilden lässt. Trotzdem IST es kein Vektor und für viele Anwendungen macht die Interpretation als Vektor auch gar keinen Sinn. Warum also nach außen öffentlich machen, dass man intern die Werte als Vektor speichert?



  • LesenBildet schrieb:

    Nochmal: Weil du gar nicht weißt, dass das Objekt intern ein Vektor speichert.

    Doch in dem Fall schon, deswegen ist auch das Beispiel unpassend. Entweder Foo ist ein Vektor und hat deswegen auch getX bis getZ. Oder Foo hat einen Vektor, dann kann man den Vektor aber auch Vektor sein lassen.

    LesenBildet schrieb:

    Du meinst, bei trivialen Gettern/Settern kann man auch öffentliche Attribute verwenden. Ich denke, das sollte man nicht machen, weil man sich so die Flexibiltät raubt, später die Implementation zu ändern.

    Die Ausgangsfrage war eigentlich, ob man die geschützte Klasseninterna über eine Referenz der Öffentlichkeit preis geben soll? Dass dies bei reinen Containern Sinn macht ist verständlich. Aber wie weit ist die Sache noch davon entfernt einfach die Attribute public zu machen? Und da war und ist meine Ansicht, dass es keinen Unterschied gibt. Das selbe steht auch im verlinkten Artikel von brotbernd. Das heißt jetzt nicht, dass ich meine Attribute public mache. Nur gebe ich auch keine Attribute per non-const-Referenz aus der Klasse.



  • Aber wie weit ist die Sache noch davon entfernt einfach die Attribute public zu machen?

    Weit. Ein schönes Beispiel hat otze mit den Quarternionen noch gebracht.

    Das selbe steht auch im verlinkten Artikel von brotbernd.

    Wo steht das bitte? Dort steht klipp und klar, dass man Attribute außer in Außnahmefällen nicht public machen sollte.
    Der Außnahmefall ist z.B. std::pair, das lediglich ein Sammelobjekt ist.#

    Das heißt jetzt nicht, dass ich meine Attribute public mache.

    Dann verstehe ich ehrlich gesagt nicht, wogegen du die ganze Zeit argumentierst. Attribute gehören private. Punkt. Außnahmen bestätigen die Regel (siehe std::pair).

    Nur gebe ich auch keine Attribute per non-const-Referenz aus der Klasse.

    Mache ich auch selten, kann aber komfortabel sein, wenn ich sowieso nicht überprüfe was der User mit meiner Klasse anstellt.
    Und selbst in so einem Fall hast du immer noch mehr Flexibiltät, als wenn du es über public member machen würdest (siehe Quaternionen-Beispiel von otze).
    Die Frage bleibt: Warum sollte man diese Flexibiltät aufgeben? Zu Gunsten von einem ()?



  • LesenBildet schrieb:

    Weit. Ein schönes Beispiel hat otze mit den Quarternionen noch gebracht.

    Ich kann an dem Beispiel keine Notwendigkeit einer non-const Referenz erkennen. Er hat damit zeigen wollen, dass es für ihn sinnvoll ist nicht das Objekt, sondern seine Attribute nach außen zu führen.

    LesenBildet schrieb:

    Wo steht das bitte? Dort steht klipp und klar, dass man Attribute außer in Außnahmefällen nicht public machen sollte.

    Und weiterhin steht da auch, warum. Weil es keinen Unterschied macht, ob ich die Referenz raus gebe oder das Attribut. Allerdings macht es schon einen Unterschied. Um den Effekt des inlinings mitnehmen zu können, der hierfür Voraussetzung ist, muss ich die Implementation im Header vornehmen.

    LesenBildet schrieb:

    Dann verstehe ich ehrlich gesagt nicht, wogegen du die ganze Zeit argumentierst. Attribute gehören private. Punkt.

    Ich argumentiere dagegen, dass man dieses private bricht, wenn man eine non-const Referenz raus gibt. Wenn ich böse bin, kann ich über diese Referenz das Attribut verändern, ohne das es vorgesehen ist. Bei einem std::vector mache ich das ständig, dort ist es aber auch beabsichtigt.

    LesenBildet schrieb:

    Mache ich auch selten, kann aber komfortabel sein, wenn ich sowieso nicht überprüfe was der User mit meiner Klasse anstellt.

    Durch eine Trennung von Getter und Setter kann ich kontrollieren, wie der Zugriff geschieht. Wenn ich mit einer Referenz arbeite, kann ich auch wenn nur unabsichtlich das Objekt ändern.



  • Ich argumentiere dagegen, dass man dieses private bricht, wenn man eine non-const Referenz raus gibt. Wenn ich böse bin, kann ich über diese Referenz das Attribut verändern, ohne das es vorgesehen ist. Bei einem std::vector mache ich das ständig, dort ist es aber auch beabsichtigt.

    Ähh...ich gebe keine non-const Referenz raus, wenn ich nicht will, dass jemand an den Interna rumpfuscht?

    class Foo{
       private;
           int x;
       int getX()const{return x;}
       int& getX(){return x};//entfernen und schwupps ist x von außen unveränderlich
    };
    

    Das ist genauso, wie ich keinen setter implementiere, wenn ich nicht will, dass jemand die Variable verändert. Also, was ist dein Punkt? Genauso wie das inlining Argument. das Selbe hast du auch bei jedem "normalen" getter oder setter. Wieder: was ist dein Punkt?

    Oder reden wir hier etwa darüber, dass uns vielleicht ein Takt verloren gehen könnte, weil wir etwas nicht im Konstruktor machen, was nicht in den Konstruktor gehört? In dem Fall können wir auch direkt Assembler programmieren.



  • otze schrieb:

    Ähh...ich gebe keine non-const Referenz raus, wenn ich nicht will, dass jemand an den Interna rumpfuscht?

    Genau das ist mein Punkt, dass die non-const Referenz als allgemeingültige Lösung angeboten wurde, was sie aber nicht ist.

    otze schrieb:

    Genauso wie das inlining Argument. das Selbe hast du auch bei jedem "normalen" getter oder setter. Wieder: was ist dein Punkt?

    Das inlining-Argument bezieht sich lediglich darauf, dass man sich nur dadurch den Funktionsaufruf sparen kann. Was für ein "3. Example 2(b) costs nothing." notwendig ist.

    otze schrieb:

    Oder reden wir hier etwa darüber, dass uns vielleicht ein Takt verloren gehen könnte, weil wir etwas nicht im Konstruktor machen, was nicht in den Konstruktor gehört?

    Falls wir jetzt wieder an der Eingangsfrage des Thread-Erstellers gelandet sind, so bin ich der Meinung, dass alle Parameter, die ein Objekt auszeichnen, von Anfang an definiert sein sollten. Wenn ich ein Objekt nur mit Defaults initialisieren wollte und später dann über Setter bilden will, dann benötige ich auch keine Konstruktoren. Ganz abgesehen davon, dass ich über die Setter Manipulationsmöglichkeiten bieten kann, die ich eigentlich gar nicht will.

    otze schrieb:

    In dem Fall können wir auch direkt Assembler programmieren.

    Wie kommst du jetzt auf Assembler?



  • Als Beispiel für non-const-Referenz-Getter werfe ich noch Lazy Evaluation ein. Angenommen man hat eine Datenbank und einen objektrelationalen Mapper. Dann kann man meist sowas machen:

    // User mit mehr als 1000 Beiträgen, deren Name mit "C" beginnt, Syntax ist mir jetzt relativ egal
    vector<User> users = User.find_by_name("C*").find_by_post_count(greater_than, 1000);
    for(user in users.hits())
    {
        user.set_post_count(0);
    }
    
    ...
    
    transaction {
        for user in users.hits()
        {
            users.save();
        }
    }
    

    Wäre hits ein öffentliches Datenmember, müsste nach jedem find_by_* die Datenbank abgefragt werden, denn man kann den Zugriff darauf nicht kontrollieren und weiß nicht, ab wann man die Daten wirklich vorhalten muss (es gibt auch Fälle, da ist es sogar relativ unwahrscheinlich, dass man die Daten überhaupt anfordern/berechnen muss). Mit ner Getter-Funktion kann man dagegen Folgendes machen:

    class User
    {
    public:
        vector<User>& hits()
        {
            if(!alreadyAskedDatabase)
                askDatabase();
            return hits_;
        }
    
    private:
        vector<User> hits_;
    };
    


  • Nick Unbekannt schrieb:

    Genau das ist mein Punkt, dass die non-const Referenz als allgemeingültige Lösung angeboten wurde, was sie aber nicht ist.

    Strange. Die Behauptung wäre mir aufgefallen. Von mir kam die nicht. Gibts ein Zitat?
    //edit ahh jetzt weiß ich was du meinst:
    Richtig ist, dass dieses Idiom immer dem getX() setX(..) überlegne ist, weil schöner. UDer zitierte Teil geht hauptsächlich auf den Vergleich mit public Membern ein. Und da gibt es genug schwerwiegendere Argumente als: "wenn ich das dumm implementiere, kann das einen Takt langsamer werden". Insbesondere in der templatisierten Welt von C++ ist "kann nicht geinlined werden" ein sehr schwaches Argument.

    Falls wir jetzt wieder an der Eingangsfrage des Thread-Erstellers gelandet sind, so bin ich der Meinung, dass alle Parameter, die ein Objekt auszeichnen, von Anfang an definiert sein sollten.

    Nein. Nach Aufruf des Konstruktors sollte sich das Objekt in einem benutzbaren Zustand befinden, es muss aber noch nichts sinnvolles leisten können. Es ist unlogisch mehr zu fordern, weil die Informationen nicht immer alle sofort verfügbar sind.
    Hier ist es natürlich Geschmackssache, wie viel genau man fordert, meine Regel ist: Solange das Objekt bei Benutzung keinen Segfault auslöst oder undefiniertes verhalten produziert, ist alles in Ordnung.
    Meistens führt dies dazu, dass Interfaces recht übersichtlich werden. Funktionen mit mehr als 5 Parametern sind schlecht zu begreifen. Bei 15 Parametern wie hier hört es dann komplett auf, das überblickt niemand, ob da jetzt 2 Parameter vertauscht sind oder nicht. Ich würde auch mal ganz dreist behaupten: hätte der Konstruktor nur 5 Parameter und dafür 3 zusätzliche Setter gehabt, dann wäre dieser Thread nicht erstellt worden.


Anmelden zum Antworten