Compiler findet Ctor nicht
-
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.
-
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.
-
Ich verstehe jetzt nicht ganz den Zusammenhang?
hits()muss auf jeden Fall eine Methode sein, weil irgendwer muss die Abfrage machen. Oder übersehe ich hier was? Was aber wesentlich entscheidender ist, deine Abfrage wird doch sicherlich eine Kopie sein?Strange. Die Behauptung wäre mir aufgefallen. Von mir kam die nicht. Gibts ein Zitat?
Ich verlinke einfach mal den Ausgangsbeitrag:
http://www.c-plusplus.net/forum/p1992219#1992219P.S. das es die Lösung ist steht jetzt nicht da, wenn man aber alle Beiträge von SchlechterCodeGefunden liest, kann man leicht den Eindruck bekommen. Zumindest ist immer die Rede von schlechter Stil und wie man es richtig macht. Von Alternativen ist genauso wenig die Rede, wie von der Frage was man eigentlich erreichen will.
Bei 15 Parametern wie hier hört es dann komplett auf, das überblickt niemand,
Sowas ähnliches habe ich auch schon gehabt, ob es jetzt 15 waren weiß ich nicht mehr, es waren zumindest viele. Ich hab mich trotzdem für die Konstruktor-Lösung entschieden, weil ich es blöd fand die Parameter erst im nachhinein in das Objekt zu geben. Dadurch kann sich leicht das Problem einschleichen, dass man etwas vergisst. Das ließe sich zwar mit einem assert abfangen, aber dann musst du wieder irgendwo definieren, wann das Objekt fertig ist. Das finde ich eine unschöne Sache, wenn es nur als Debug-Mittel benötigt wird.
Prinzipiell sollte man aber nur nach dem KISS-Prinzip vorgehen.Es geht nicht darum, ob irgendwas langsamer ist oder nicht. Die Frage dreht sich wirklich nur darum, ob es sinnvoll ist, Daten die ich vorher aufwendig versucht habe zu schützen, jetzt wieder auf die Allgemeinheit los zu lassen? Weil nichts anderes ist es, wenn ich eine Referenz auf diese raus gebe. Dass das in Fällen beabsichtigt ist steht außer Frage.
class Foo { int m_bar; public: int & bar() { return m_bar; } } int & blub = foo.bar(); blub += 42;Im Beispiel wollte ich mit dem Wert von m_bar rechnen, aber nicht m_bar verändern. Was also tun, wenn ich das & übersehen habe, weil es so unscheinbar ist oder 10 Zeilen weiter oben steht? Wenn du Pech hast suchst du nach so einen Fehler ewig lange. Dass das Beispiel nicht an den Haaren herbeigezogen ist, sollte die Regel verdeutlichen, dass eine Konstante beim Vergleich immer auf der linken Seite stehen soll.
-
Nick Unbekannt schrieb:
Ich verstehe jetzt nicht ganz den Zusammenhang?
hits()muss auf jeden Fall eine Methode sein, weil irgendwer muss die Abfrage machen.Das kann
find_by_*auch selbst erledigen. Ist aber wie gesagt nicht so sinnvoll, wenn man Abfragen durch weitere Parameter weiter einschränken will.Was aber wesentlich entscheidender ist, deine Abfrage wird doch sicherlich eine Kopie sein?
Was soll eine Kopie von was sein?
-
Die Abfrage liefert ja nur eine Untermenge von deiner Datenbank und diese wird sicherlich in eine neue Tabelle kopiert sein? Womit sich das Problem, dass die Daten verändert werden können, erledigt hätte.