Compiler findet Ctor nicht
-
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.
-
Nick Unbekannt schrieb:
Die Abfrage liefert ja nur eine Untermenge von deiner Datenbank und diese wird sicherlich in eine neue Tabelle kopiert sein?
Wie kommst du denn auf sowas? Die Abfrage kapselt nur ein
SELECTund schiebt die gefundenen Entitäten als Model-Objekte in eine Collection. Da wird nix in der Datenbank kopiert.Womit sich das Problem, dass die Daten verändert werden können, erledigt hätte.
Schau dir den Code nochmal an. Ich greife in der Transaktion auf die geänderten Objekte zu.
-
Michael E. schrieb:
Wie kommst du denn auf sowas? Die Abfrage kapselt nur ein
SELECTund schiebt die gefundenen Entitäten als Model-Objekte in eine Collection. Da wird nix in der Datenbank kopiert.Das meinte ich auch nicht. Ich meinte einfach nur die Datensätze im Ergebnis, die ja quasi eine neue Tabelle bilden.
Michael E. schrieb:
Schau dir den Code nochmal an. Ich greife in der Transaktion auf die geänderten Objekte zu.
In deinem Beispiel wird nicht so richtig ersichtlich, was wann ausgeführt wird? Auf jeden Fall lassen sich die geänderten Daten erst beim zweiten Aufruf zurück schreiben.
-
Nick Unbekannt schrieb:
Michael E. schrieb:
Wie kommst du denn auf sowas? Die Abfrage kapselt nur ein
SELECTund schiebt die gefundenen Entitäten als Model-Objekte in eine Collection. Da wird nix in der Datenbank kopiert.Das meinte ich auch nicht. Ich meinte einfach nur die Datensätze im Ergebnis, die ja quasi eine neue Tabelle bilden.
Tut mir Leid, aber das ist Kappes. Erstens kannst du hier nicht von einer neuen Tabelle sprechen. Tabellen sind in der Datenbank,
usersdagegen ist eine Menge von Objekten, die aus Datenbankeinträgen erstellt wurden. Bei dieser Erstellung können übrigens auch mehrere Tabellen berücksichtigt worden sein. Zweitens sind die Objekte zwar Kopien der Datenbankentitäten, aber um diese Kopien gehts in dem Beispiel doch gar nicht. Es geht darum, ob die Collection vonhitsby reference oder by value zurückgegeben soll.In deinem Beispiel wird nicht so richtig ersichtlich, was wann ausgeführt wird?
Von oben nach unten.
Auf jeden Fall lassen sich die geänderten Daten erst beim zweiten Aufruf zurück schreiben.
Meinst du damit, dass ich das Zurücksetzen des Post-Counters und das Speichern in der Datenbank getrennt habe? Das muss man nicht machen. Ich hätte genauso gut Folgendes schreiben können:
for(user in users.hits()) { user.set_post_count(0); user.save(); }Deshalb verstehe ich deinen Kommentar nicht.
Edit: Wenn du dich bezüglich der Ausführungsreihenfolge an den Klammern nach
transactionstörst: Das ist keine Funktionsdefinition, sondern soll die Aufgabe der Transaktion spezifizieren. Meinetwegen kannst du dir das auch so denken:Transaction t; for(user in users.hit()) { t.add(user, User::save); } t.doIt();
-
Michael E. schrieb:
Tut mir Leid, aber das ist Kappes. Erstens kannst du hier nicht von einer neuen Tabelle sprechen. Tabellen sind in der Datenbank,
usersdagegen ist eine Menge von Objekten, die aus Datenbankeinträgen erstellt wurden.Dann nennen wir es eben eine Menge von Objekten. Mir ging es lediglich um die Veranschaulichung und da kommt eine Tabelle doch sehr nah. Es ist natürlich keine Tabelle in der Datenbank gemeint.
Michael E. schrieb:
Zweitens sind die Objekte zwar Kopien der Datenbankentitäten, aber um diese Kopien gehts in dem Beispiel doch gar nicht.
Doch genau darum geht es, dass du die Veränderungen nicht an den Originaldaten vornimmst, weil die nach wie vor in der Datenbank liegen.
Michael E. schrieb:
Es geht darum, ob die Collection von
hitsby reference oder by value zurückgegeben soll.Das spielt keine Rolle, solange du immer mit den aktuellen Daten arbeitest, sprich die Daten aus der Datenbank holst.
Michael E. schrieb:
Von oben nach unten.
Das ist klar, nur wenn ich dein Beispiel richtig deute, so wird die ganze Transaktion nur ein einziges Mal ausgeführt und dabei werden auch die Datensätze in deiner Collection mit den Daten aus der DB aktualisiert?
Michael E. schrieb:
Meinst du damit, dass ich das Zurücksetzen des Post-Counters und das Speichern in der Datenbank getrennt habe?
Nein, ich meine damit, dass man mindestens zwei Aufrufe benötigt, um die Daten von außen zu ändern. Ein um sich die Referenz zu holen und einen zweiten um die von außen geänderten Daten zurück zu schreiben. Was natürlich hinfällig ist, wenn du die Daten nur cachst und nie wieder zurück schreibst.
Ich glaube aber das wir hier vollkommen aneinander vorbei reden und uns deswegen nicht verstehen.
Aber um es nochmal auf den Punkt zu bringen. Ich finde es unsinnig oder falsch dem Compiler die Möglichkeit zu nehmen über die Sichtbarkeit von Objekten entscheiden zu können, die man vorher mühselig eingebaut hat. Sprich ein private Member über eine non-const Referenz public zu machen. Das hat für mich den selben Charme wie ein const_cast. Vielleicht wird es dadurch etwas deutlicher.
-
Nick Unbekannt schrieb:
Michael E. schrieb:
Zweitens sind die Objekte zwar Kopien der Datenbankentitäten, aber um diese Kopien gehts in dem Beispiel doch gar nicht.
Doch genau darum geht es, dass du die Veränderungen nicht an den Originaldaten vornimmst, weil die nach wie vor in der Datenbank liegen.
Michael E. schrieb:
Es geht darum, ob die Collection von
hitsby reference oder by value zurückgegeben soll.Das spielt keine Rolle, solange du immer mit den aktuellen Daten arbeitest, sprich die Daten aus der Datenbank holst.
Du hast das Beispiel ganz einfach nicht verstanden.
- Nimm Daten aus der Datenbank und speichere sie inusers. Das Objektusersenthält nun die Originaldaten (um bei deiner Nomenklatur zu bleiben). Vergiss einfach für einen Moment, dass es eine Datenbank gibt. Wir wollen nur aufusersarbeiten.
- Verändere Objekte inusers. Dabei kommt es darauf an, obhitsper Referenz oder Wert zurückgibt (die eigentliche Aussage des Beispiels).
- Speichere die Daten vonusersin die Datenbank.Michael E. schrieb:
Von oben nach unten.
Das ist klar, nur wenn ich dein Beispiel richtig deute, so wird die ganze Transaktion nur ein einziges Mal ausgeführt und dabei werden auch die Datensätze in deiner Collection mit den Daten aus der DB aktualisiert?
Natürlich wird die Transaktion zum Speichern der Objekte aus
usersin die Datenbank nur einmal ausgeführt. Dabei wird nicht die Collection geändert, sondern die Datenbank!Michael E. schrieb:
Meinst du damit, dass ich das Zurücksetzen des Post-Counters und das Speichern in der Datenbank getrennt habe?
Nein, ich meine damit, dass man mindestens zwei Aufrufe benötigt, um die Daten von außen zu ändern. Ein um sich die Referenz zu holen und einen zweiten um die von außen geänderten Daten zurück zu schreiben.
Wiederum: Betrachte
usersals "Originaldaten".
-
Nick Unbekannt schrieb:
Im Beispiel wollte ich mit dem Wert von m_bar rechnen, aber nicht m_bar verändern.
Das ist mal arg konstruiert. Genausogut könnte ich bei ner const-ref rückgabe sagen: "Und was ist wenn ich da nen const_cast mal so schwupps drin stehen habe und übersehe? ja was dann?"). Oder "was wenn ich jetzt zufällig (((int*)&Foo)+4) im Programm stehen habe? ja dann kann ich sogar auf private variablen zugreifen!!!".
So ein & schlüpft nicht einfach so ins Programm. Auch wenn es klein und unscheinbar ist, man muss schon sehr wirr sein, um nicht zu merken, während man es tippt. Und es kommt so selten in so einer Form im Code vor, dass man es nicht einfach so übersieht. Und überhaupt: diese Notation foo.bar() ist genau dazu _da_ dass man keine extra Variablen anlegen muss. Es ist einfach kurz und aussagekräftig genug.
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.
Auch diese Regel habe ich nie in der Praxis angewendet gesehen. Sie ist auch unnütz, da man viel öfter Vergleiche der Form var1 == var2 als var1 == konstante hat.