Frage zum Syntax für Ctor mit Initialisierung
-
Für mich ist es eine Unzulänglichkeit das der Compiler dies nicht erkennt, also dort im Konstruktor unterscheidet.
-
Fellhuhn schrieb:
Für mich ist es eine Unzulänglichkeit das der Compiler dies nicht erkennt, also dort im Konstruktor unterscheidet.
Das kann der Compiler oft gar nicht, weil dein Ctor ja beliebig kompliziert sein soll? Wenn deine Klasse ein paar Member hat, dann muessen von allen Membern die Ctors aufgerufen werden, von diesen Members wieder die Ctors, und von diesen wieder, und und und und.... Du kannst vom Compiler nicht erwarten dass er so tief in die Vererbungshierarchie schaut und erkennt, dass keiner der Ctor-Aufrufe einen Effekt hat, bzw. der Effekt einige hundert/tausend Maschinenbefehle weiter wieder ueberschrieben wird, ohne dass zwischendrin auf die Ergebnisse zugegriffen wird.
-
Fellhuhn schrieb:
Das würde bei mir dann eher unter schlecht aufgebaute Klasse und Unzulänglichkeit des Standards/Compilers fallen. Initialisierungslisten zerrupfen den ganzen Konstruktor und sorgen mE so für eine schlechtere Übersicht. Daher minimiere ich die Verwendung wo es nur geht. Gab bisher nie Probleme.
Wo zerrupfen Initialisierungslisten bitteschön den Konstruktor? Es ist doch nur eine Aufzählung der einzelnen Member mit ihren Initialisierungslisten (Noch Übersichtlicher kann man es imho nicht machen). Ich glaube eher das dich die Schreibweise verstört.
Zum zweiten: Ich habe sehr häufig den Fall das ich keinen Standardkonstruktor (und teilweise auch keinen Zuweisungsoperator/Kopierkonstruktor) definiere. Ein Objekt muss meines Erachtens immer einen konsistenten Stand aufweisen, nicht selten reichen Standardkonstruktoren dafür nicht aus. Ich sehe es daher eher als falsches Design der Klasse an wenn man dessen Benutzung vorschreibt. Und auf den Compiler/Standard kannst du das Problem auch nicht schieben, da du im Konstruktorrumpf selber schon erwartest das dieses Objekt vollständig existiert.
Zu guter Letzt gibt es Fälle wo du an die Initialisierungsliste nicht vorbeikommst:
a) Initialisierung von Instanzgebundenen Konstanten
b) Verwendung von Objekten ohne Standardkonstrukor
c) Verwendung von anderen als dem Standardkonstruktor der Basisklasse
d) Übersicht und Exceptionsicherheit bei mehreren dynamisch allozierenden Membern (Dies geht mit Smartpointern sehr schön)cu André
-
Fellhuhn schrieb:
Für mich ist es eine Unzulänglichkeit das der Compiler dies nicht erkennt, also dort im Konstruktor unterscheidet.
Das tut er in meinen Augen aus gutem Grund nicht. Das einfachste Beispiel sind Referenzen. Der Compiler müsste hier unterscheiden, dass die erste Zuweisung die Referenz setzt, und jede weitere den operator= des dahinterliegenden Objektes aufruft. Daraus ergäben sich wesentlich mehr Probleme für die Compilerhersteller als mit der Initialisierungsliste.
Man darf auch nicht vergessen, dass viele Sprachen ohne Initialisierungslisten wie z.B. Java mit Referenzen arbeiten, sprich es gibt hier keine uninitialisierten Objekte. Uninitialisierte Referenzen sind per Definition null und können damit auch belegt werden, nachdem bereits Anweisungen im Konstruktor durchgeführt wurden. C++ setzt voraus, dass zu Beginn des Konstruktor-Körpers bereits alle Membervariablen initialisiert sind.
-
asc schrieb:
Wo zerrupfen Initialisierungslisten bitteschön den Konstruktor? Es ist doch nur eine Aufzählung der einzelnen Member mit ihren Initialisierungslisten (Noch Übersichtlicher kann man es imho nicht machen). Ich glaube eher das dich die Schreibweise verstört.
Die Reihenfolge der Elemente in der Liste ist ja vorgegeben durch die Reihenfolge der Definition der Variablen in der Klasse. Daher sind hier Abhängigkeiten nicht immer aufzulösen (wenn sich diese je nach Konstruktor unterscheidet).
Desweiteren sind komplexere Berechnungen im Konstruktor durchaus möglich die als Parameter an den Konstruktor von Membervariablen übergeben werden müssen. Das trennt das Ganze.
Oder eben Memberpointer den Werte dynamisch zugewiesen werden. Finde ich doch sehr unschön.
-
Fellhuhn schrieb:
...Initialisierungslisten zerrupfen den ganzen Konstruktor und sorgen mE so für eine schlechtere Übersicht. Daher minimiere ich die Verwendung wo es nur geht. Gab bisher nie Probleme.
Erst Zweiteres (also Dein Umgang mit Initialisierungslisten) führt zu Ersterem.
Wenn Du Dir rechtzeitig angewöhnt hättest, Alles, was möglich ist, in Initialisierungslisten zu packen, hättest Du eine ganz klare und auch saubere Trennung zwischen Initialisierungen (in der gleichnamigen Liste) und sonstiger Fachlichkeit (im Ctor-Rumpf).
Und was die Reihenfolge angeht, finde ich es sowieso besser, wenn Abhängigkeiten zwischen Membern (und genau DIE geben eine Reihenfolge in der InitListe vor) auch in der Klassendefinition auftauchen.
Übrigens: Auch im Ctor-Rumpf selbst wirst Du diese Reihenfolge einhalten müssen...Gruß,
Simon2.
-
e) Initialisierung von Referenzen
-
Simon2 schrieb:
Übrigens: Auch im Ctor-Rumpf selbst wirst Du diese Reihenfolge einhalten müssen...
Aber mit Initialisierungslisten kannst du nicht zwei Konstruktoren haben wo einmal A von B und einmal B von A abhängig ist. Ansonsten geht das ohne Probleme.
Was den bisherigen Umgang angeht, so kann ich da nichts dran ändern. Denn ich erstelle keine neuen Projekte, sondern arbeite nur an bestehenden mit (sprich: auf der Arbeit). Zum privaten Programmieren komme ich schon seit Jahren nicht mehr.
-
Fellhuhn schrieb:
Aber mit Initialisierungslisten kannst du nicht zwei Konstruktoren haben wo einmal A von B und einmal B von A abhängig ist. Ansonsten geht das ohne Probleme.
Darf ich mal ein Beispiel sehen was du damit meinst? Nichts für ungut, aber zumeist gibt es immer eine vorgegebene "Navigationsrichtung" bei Objekten, und ggf. setzt man später noch beim einen Objekt einen Verweis auf das andere wenn gegenseitige Navigation nötig ist.
Initialisierungslisten verwenden heißt nicht, das man den Konstruktorrumpf garnicht mehr verwendet (wenn gleich er bei mir meistens, aber nicht immer, leer ist).
Fellhuhn schrieb:
Was den bisherigen Umgang angeht, so kann ich da nichts dran ändern. Denn ich erstelle keine neuen Projekte, sondern arbeite nur an bestehenden mit (sprich: auf der Arbeit). Zum privaten Programmieren komme ich schon seit Jahren nicht mehr.
Ich behaupte das nicht wenige von den Schreibern auch arbeiten, aber einmal geschriebener Code darf auch von Zeit zu Zeit angepasst und verbessert werden (Thema: Refactoring).
cu André
-
asc schrieb:
Darf ich mal ein Beispiel sehen was du damit meinst? Nichts für ungut, aber zumeist gibt es immer eine vorgegebene "Navigationsrichtung" bei Objekten, und ggf. setzt man später noch beim einen Objekt einen Verweis auf das andere wenn gegenseitige Navigation nötig ist.
Zum Beispiel:
class A{ public: A(int a){ //... } int calculateWhatever(); }; class B{ public: B(int b){ // ... } int calculateWhatever(); }; class C{ public: C(){ a = A(10); b = B(a.calculateWhatever()); } C(int i){ b = B(10); a = A(b.calculateWhatever()); } A a; B b; };oder eben die Reihenfolge abhängig von einem Konstruktorparameter etc.
Initialisierungslisten verwenden heißt nicht, das man den Konstruktorrumpf garnicht mehr verwendet (wenn gleich er bei mir meistens, aber nicht immer, leer ist).
Eben. Und in dem Fall ist es getrennt, was ich sehr unschön finde.
Ich behaupte das nicht wenige von den Schreibern auch arbeiten, aber einmal geschriebener Code darf auch von Zeit zu Zeit angepasst und verbessert werden (Thema: Refactoring).
In dem Bereich in dem ich arbeite ist Refactoring per Anweisung verboten. Es ist eine Fehlerquelle die niemand bezahlt.
-
Das Beispiel sieht mir dann doch etwas arg konstruiert aus. Und selbst dann koennen noch 3 von den 4 Zuweisungen in die Initilaisierungsliste, wobei ich vielleicht auch einen trotzdem in den Rumpf setzen wuerde:
class C{ public: C() : a(10), b() { b = B(a.calculateWhatever()); //kann auch in initliste } C(int i) : a(), b(10) { a = A(b.calculateWhatever()); } A a; B b; };Ich leg mir eigentlich immer eine vollstaendige Initialisierungsliste an, allein um sicher zu gehen, dass ich auch tatsaechlich alle Member initialisiere. Ich hasse es naemlich wenn irgendwelche Fehler auftauchen, nur weil ich was uebersehen hab und irgedein int-member mit komischen "zufalls"-Werten initialisiert wurde...
Das setzen von b in C::C() habe ich hier deshalb in den Ctor-Rumof gezogen, weil in gewisser Hinsicht dort etwas mehr passiert als nur eine stumpfe initialisierung, naemlich etwas, das abhaengig ist von einem anderen Member, und das kann boese enden wenn da jemand nichtsahnend unten die Memberdeklarationen vertauscht.Fellhuhn schrieb:
In dem Bereich in dem ich arbeite ist Refactoring per Anweisung verboten. Es ist eine Fehlerquelle die niemand bezahlt.
Dann sehen eure Chefs das aber etwas sehr einseitig. ihr seid also per Weisung dazu verpflichtet, veralteten und eventuell fehldesignten Code mit all seinen Fehlern weiter zu verwenden (refactoring ist ja dazu da, sowas zu beheben)? Natuerlich kostet Refactoring Zeit und es schleichen sich Fehler ein, die erst debuggt werden sollten. Trotzdem stellt sich die Frage, ob das rumwerkeln mit Patchwork-Code, unuebersichtlichen Hacks und Zusaetzen nicht mehr Zeit (udn Nerven) kostet. Aber das scheint wohl Firmenpolitik zu sein...
-
Fellhuhn schrieb:
In dem Bereich in dem ich arbeite ist Refactoring per Anweisung verboten. Es ist eine Fehlerquelle die niemand bezahlt.
Ich finde gammeligen Code wo immer nur Flicken rumgeklebt werden eine viel größere Fehlerquelle (und im Endeffekt teuerer) als ein Code wo man auch noch umgestalltet um ihn besser lesen und warten zu können. Aber das ist Firmenentscheid.
Alle meine bisherigen Erfahrungen haben diese Aussage bislang untermauert.
Zu deinem Beispiel: Solche Fälle hatte ich bislang noch nicht, und zumindestens in meinen Bereich wüsste ich auch auf Anhieb keinen Anwendungsfall für sich ändernde Abhängigkeiten. Lösen würde ich es aber wohl durch eine Ebene der Indirektion, hängt natürlich auch von der Häufigkeit der Aufrufe ab, wobei man dem gegenüber auch die Kosten der zusätzlichen Konstruktionen und Zuweisungen in eurem Code gegenüber stellen muss.
cu André
-
pumuckl schrieb:
Fellhuhn schrieb:
In dem Bereich in dem ich arbeite ist Refactoring per Anweisung verboten. Es ist eine Fehlerquelle die niemand bezahlt.
Dann sehen eure Chefs das aber etwas sehr einseitig. ihr seid also per Weisung dazu verpflichtet, veralteten und eventuell fehldesignten Code mit all seinen Fehlern weiter zu verwenden (refactoring ist ja dazu da, sowas zu beheben)? Natuerlich kostet Refactoring Zeit und es schleichen sich Fehler ein, die erst debuggt werden sollten. Trotzdem stellt sich die Frage, ob das rumwerkeln mit Patchwork-Code, unuebersichtlichen Hacks und Zusaetzen nicht mehr Zeit (udn Nerven) kostet. Aber das scheint wohl Firmenpolitik zu sein...
Sehe ich ähnlich... Naja, das hat mir Anfangs den Ruf eingebracht langsam zu sein, inzwischen hat aber der Chef festgestellt das ich im Endeffekt vielleicht doch der schnellste bin, da mein Code üblicherweise weniger Fehler und Wartung nach sich zieht. Ganz davon "verschwende" ich meine Zeit auch noch mit *igitt* Dokumentation was ja noch fast schlimmer ist ;p
cu André
-
@pumuckl
Und schon ist es getrennt, was mir eben nicht gefällt. Dazu noch einiges an Kommentaren und anderen Berechnungen und man verliert schnell den Überblick. Und gerade in Initialisierungslisten finde ich Kommentare doch sehr ... störend.Was das Refactoring angeht, so sehe ich das auch so. Nur kann ich natürlich auch die Argumentation der Führungsriege nachvollziehen. Schließlich bekommt man Geld für Features und Support. Und wenn es um Aufträge im siebenstelligen Bereich geht, dann wird man den Teufel tun und bewährte Frickelarbeit umschreiben.
-
Hätte nicht gedacht das ich hier so eine Diskussion lostretet.
Mir war bewust, das die Initialisierungsliste das ist, was ich will. Aber ich brauche auch den ctor Körper. Genau wie Fellhuhn bin ich der Meinung das der nichts in der Deklaration zu suchen hat. Das du, lieber Fellhun soviel viele Gegenstimmen bekommen hast liegt meines Erachtens hauptsächlich daran, dass du es nicht richtig begründet hast, bzw. die Initialisierungsliste abgelehnt hast, obwohl sie in der Deklaration nur an falscher Stelle steht: Es ist guter Stil die Deklaration säuberlich von der Definition zu trennen. Da die Initialisierungsliste teil der Definition ist, gehört sie in den Definitionsteil. Das dies geht musste ich erst anhand eines Nebensatzes von LordJaxom lernen:
LordJaxom schrieb:
Nunja, Du hast hier zwei Methodenkörper, einmal den leeren innerhalb der Klasse und einmal den mit der Zuweisung ausserhalb der Klasse. Lösung: Du wirfst einen davon weg. Du könntest z.B. die Initialisierungsliste in den Konstruktor ausserhalb der Klasse verfrachten.
Vorher dachte ich die Initialisierungsliste kann nur in der Deklaration stehen und habe deshalb krampfhaft versucht den Body von dieser Liste zu trennen. Was sinnvoller weise nicht geht.
LordJaxom schrieb:
BTW: Du kannst die Zuweisung von m_nameB auch in die Initialisierungsliste verfrachten. Aber warum brauchst Du überhaupt in beiden Klassen je eine Kopie des Strings?
Das habe ich mich auch gefragt. Bei meinen eigentlichen Code benötige ich den String sogar an drei Stellen, da das Parent Objekt zwei Memberobjekte hat, die beide den String brauchen. Wäre das nicht so, könnte ich den String in Childobjekt ermitteln und es von dort den Parent zur Verfügung stellen. So geht das aber nicht und das die Childobjekte sich den String von ihren Parent holen ist nicht ohne weiteres möglich, da sie ja Kontextunabhängig arbeiten sollen, also nichts von ihren Parent wissen sollen.
LordJaxom schrieb:
EDIT:
Zu allgemeinen Frage: Da hilft nur Sprachverständnis und Lesen der Fehlermeldung. In der Fehlermeldung steht ja, was falsch ist. Wenn man nun weiß, dass es nur einen Methodenkörper geben darf, und wie Methodenkörper und Initialisierungsliste auszusehen haben, ergibt sich die Lösung von selbst
Irgendwie beisst sich die Katze da selber in den Schwanz. Gute Syntax-Literatur zu haben wäre da schon hilfreich. So werde ich wohl noch öfters mit meinen dummen Fragen hier auftreffen. Schön zu wissen das hier große Diskussionsbereitschaft besteht.
Danke an alle
BerndP.S. (out of topic):
Fellhuhn schrieb:
In dem Bereich in dem ich arbeite ist Refactoring per Anweisung verboten. Es ist eine Fehlerquelle die niemand bezahlt.
Das kann ich sehr nachfühlen. Mein Chef kennt zwar Refactoring überhaupt nicht, aber hier ist es so, das der Programmierer als die Fehlerquelle angesehen wird. Was dazu führte das es mein Privatvergnügen wurde, diese zu beseitigen. Statt mich um bessere Arbeitsorganisation zu kümmern, habe ich mich immer extensiver in die Arbeit gestürzt (Hobby und Beruf war eins). Nun bin ich mit mein BurnOut-Syndrom in der 4. Phase (1. Phase: Euphorie; 2. Phase: Stagnation; 3. Phase: Frustration; 4. Phase Resignation). Ab der zweiten Phase hätte ich begreifen können, dass etwas schief läuft. Ich bin aber wie auf Schienen in die Katastrophe hineingerast. Erst danach habe ich die Sinnlosigkeit meines Handelns begriffen (und zwar für beide Seiten, den mein Chef wollte nicht, das ich ausbrenne). MACHT NICHT DEN SELBEN FEHLER! Es gibt so viele schöne Dinge die man in seinen Leben tun sollte, solange man sie noch tun kann, darum: Wenn Feierabend ist, einfach PC abschalten, Gedanken umschalten (Familie, Freunde, Sport, Outdoor Hobbys etc.) und gehen..
Googelt mal den Begriff BurnOut-Syndrom. Da gibt es Tests im Internet. Wenn da mehr als 80% der Punkte auf einen von euch zutreffen, dann ist es höchste Zeit die Notbremse zu ziehen. Ich schreibe das hier, weil ich glaube das unter Informatiker ein großes Gefährdungspotential besteht, besonders wenn keine Familie da ist, die ein auf andere Gedanken bringt. Kinder sind da ideal. Natürlich nur, wenn man sie auch annimmt, statt sie nur irgendwo abzustellen.
Ok, muss weiter...
-
BerndD schrieb:
Mir war bewust, das die Initialisierungsliste das ist, was ich will. Aber ich brauche auch den ctor Körper.
Nichts gegen den Konstruktorrumpf, aber den brauch man in einer sauberen Initialisierung nunmal in den seltesten Fällen. Zum einen ist das von Fellhuhn aufgeführte Beispiel ein extremer Sonderfall, der mir in nunmehr knapp 10 Jahren C++ nicht einmal in der Praxis aufgetreten ist, zum anderen hat die Verwendung der Initialisierungsliste deutliche Vorteile.
BerndD schrieb:
Genau wie Fellhuhn bin ich der Meinung das der nichts in der Deklaration zu suchen hat. Das du, lieber Fellhun soviel viele Gegenstimmen bekommen hast liegt meines Erachtens hauptsächlich daran, dass du es nicht richtig begründet hast, bzw. die Initialisierungsliste abgelehnt hast, obwohl sie in der Deklaration nur an falscher Stelle steht...
Also ich bin wohl beim lesen Blind hier auf ein Fehler in seinen Argumenten zu finden, ich habe eigentlich nur und ausschließlich von ihm eine Ablehnung gelesen. Und auch Deklaration erwähnt er mit keinen Wort. Nichts für ungut, ja es mag Gründe geben auch den Methodenrumpf des Konstruktors zu verwenden, aber dies ist die Seltenheit, nicht die Regel (Zumal man im Konstruktor eh sehr vorsichtig sein muss Methoden aufzurufen, oder sicherstellen muss das keine davon virtuell ist und die gewünschte Funktionalität in der abgeleiteten Klasse steckt).
BerndD schrieb:
Es ist guter Stil die Deklaration säuberlich von der Definition zu trennen. Da die Initialisierungsliste teil der Definition ist, gehört sie in den Definitionsteil.
Kein Wiederspruch.
BerndD schrieb:
LordJaxom schrieb:
BTW: Du kannst die Zuweisung von m_nameB auch in die Initialisierungsliste verfrachten. Aber warum brauchst Du überhaupt in beiden Klassen je eine Kopie des Strings?
Das habe ich mich auch gefragt. Bei meinen eigentlichen Code benötige ich den String sogar an drei Stellen, da das Parent Objekt zwei Memberobjekte hat, die beide den String brauchen. Wäre das nicht so, könnte ich den String in Childobjekt ermitteln und es von dort den Parent zur Verfügung stellen. So geht das aber nicht und das die Childobjekte sich den String von ihren Parent holen ist nicht ohne weiteres möglich, da sie ja Kontextunabhängig arbeiten sollen, also nichts von ihren Parent wissen sollen.
Das verstehe ich jetzt leider nicht. Zumindestens nicht warum du ihn in der abgeleiteten Klasse auch noch benötigst. Wenn der Parent Membervariablen hat die diesen String benötigen, ist der Parent von dem String eh abhängig, so das die Verwaltung des String IMHO in den Bereich des Parents fällt. Die abgeleitete Klasse ist aber mit der Parentklasse identisch mit Ausnahme das sie die Parentklasse ergänzt. Daher wäre es nur logisch, hier auf den String in der Parentklasse zurückzugreifen.
BerndD schrieb:
Irgendwie beisst sich die Katze da selber in den Schwanz. Gute Syntax-Literatur zu haben wäre da schon hilfreich. So werde ich wohl noch öfters mit meinen dummen Fragen hier auftreffen. Schön zu wissen das hier große Diskussionsbereitschaft besteht.
Wenn du wirklich sauberen Code schreiben willst seien dir sowohl Herb Sutter[1-4] als auch Scott Meyers [6-7] ans Herz gelegt. Ersterer hat ein Buch [1] mit Regeln zusammengestellt, wenn gleich die Ausführlichkeiten und damit auch die Aha-Effekte nicht mit seiner Exceptional-Reihe [2-4] zu vergleichen sind. Ein Teil der Exceptionalreihe gibt es in verkürzter Form im Guru of the Week [8] (Seine Bücher sind Vertiefungen und Ergänzungen der dort aufgeführten Probleme)
cu André
[1] Herb Sutter [Addison-Wesley], C++ Coding Standards: 101 Rules, Guidelines and Best Practices (C++ In-Depth): Rules, Guidelines, and Best Practices
[2] Herb Sutter [Addison-Wesley], Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions
[3] Herb Sutter [Addison-Wesley], More Exceptional C++: 40 New Engineering Puzzles, Programming Problems, and Solutions
[4] Herb Sutter [Addison-Wesley], Exceptional C++ Style: 40 New Engineering Puzzles, Programming Problems and Solutions (C++ In-Depth Series)
[5] Scott Meyers [Addison-Wesley], Effective C++: 55 Specific Ways to Improve Your Programs and Designs
[6] Scott Meyers [Addison-Wesley], More Effective C++: 35 New Ways to Improve Your Programs and Designs
[7] Scott Meyers [Addison-Wesley], Effective STL: 50 Specific Ways to Improve the Use of the Standard Template Library
[8] http://www.gotw.ca/gotw/index.htm
-
Ich bezog mich auf Fellhuhn Aussage:
Fellhuhn schrieb:
Initialisierungslisten zerrupfen den ganzen Konstruktor und sorgen mE so für eine schlechtere Übersicht.
Ich gehe davon aus, das er damit die Deklaration meinte. Damit hat er meines Erachtens recht und du bestätigst das ja auch – Die Initialisierungsliste gehört nicht in die Deklaration. Leider hatte ich sie noch nicht oft bei der Definition stehen sehen, so das es zu meiner "Denkblockade" kam. Die einzige Ausnahme die ich da machen würde, wäre wenn eine Klasse so einfach aufgebaut ist, das man sie der Übersicht wegen komplett in die Header-Datei schreiben kann. Alle weiteren Argumentationen von Fellhuhn wurden schon ausdiskutiert. Wobei ich nicht versucht habe den "Sonderfall"-Beweis nachzuvollziehen. Als C++ ungeübter ist mir das zu aufwändig. Das es keine Probleme beim nicht verwenden von Initialisierungslisten gibt, ist verständlich. Man ist einfach nur ineffizienter, weil vieles doppelt initialisiert wird. Benutzt werden die Klassen erst danach. Das man alten Code nicht anfasst ist gängige Praxis, schließlich müsste man dafür dann auch den Kopf hinhalten. So kann man sagen: Ist nicht meine Schuld, mein Vorgänger war es.
Anderseits vertrittst du das andere Extrem, nämlich das der Konstruktorrumpf eigentlich überflüssig sei. Das ist er aber nur, wenn sich das Objekt vollständig über seine ctor-Parameter konfigurieren lässt. Ich übertreibe mal um die Schwächen dieses Extrems zu verdeutlichen: Ein ctor mit 20 Parametern, nur weil man glaubt vielleicht irgendwann einmal auch auf den letzten Parameter Einfluss haben zu müssen, ist viel ineffizienter als ein Objekt das sich aus wenigen Parametern heraus zu 90% aller Anwendungsfälle richtig konfiguriert. Die restlichen Parameter können dann bei Bedarf im Konstruktorrumpf gesetzt werden. Besonders die, bei deren setzen nicht ein starker Umbau ausgelöst wird.
asc schrieb:
BerndD schrieb:
LordJaxom schrieb:
BTW: Du kannst die Zuweisung von m_nameB auch in die Initialisierungsliste verfrachten. Aber warum brauchst Du überhaupt in beiden Klassen je eine Kopie des Strings?
Das habe ich mich auch gefragt. Bei meinen eigentlichen Code benötige ich den String sogar an drei Stellen, da das Parent Objekt zwei Memberobjekte hat, die beide den String brauchen. Wäre das nicht so, könnte ich den String in Childobjekt ermitteln und es von dort den Parent zur Verfügung stellen. So geht das aber nicht und das die Childobjekte sich den String von ihren Parent holen ist nicht ohne weiteres möglich, da sie ja Kontextunabhängig arbeiten sollen, also nichts von ihren Parent wissen sollen.
Das verstehe ich jetzt leider nicht. Zumindestens nicht warum du ihn in der abgeleiteten Klasse auch noch benötigst. Wenn der Parent Membervariablen hat die diesen String benötigen, ist der Parent von dem String eh abhängig, so das die Verwaltung des String IMHO in den Bereich des Parents fällt. Die abgeleitete Klasse ist aber mit der Parentklasse identisch mit Ausnahme das sie die Parentklasse ergänzt. Daher wäre es nur logisch, hier auf den String in der Parentklasse zurückzugreifen.
Vielleicht gibt es ein Verständigungsproblem, weil ich als Delphi-Programmierer Begriffe anders verwende. Wenn ich von Parentobjekt spreche, hat das nichts mit Ableiten (in sinne von Vererben) zu tun. Der Parent ist das Objekt, das die Child-Objekte als Membervariablen enthält. In C++ sagt man vielleicht Container dazu, obwohl das IMHO nur für Klassen richtig ist, die zur Laufzeit eine unbestimmte Menge vom Objekten aufnehmen kann.
In Delphi hat zum Beispiel jede Klasse eines GUI-Elementes einen "Parent"-Zeiger auf das in der Fensterhirachie übergeordnete Objekt. Das ist dort sicher auch sinnvoll, aber für meine C++ Klassen will ich das vermeiden, weil es Abhängigkeiten schafft. In Fall des Strings würde das untergeordnete Objekt nur funktionieren, wenn es einen Zeiger auf ein Parent besitzt, der den String bereitstellt. Das würde diese Klasse sehr entwerten. Ich sah es deshalb als notwendiges Übel an, den String dreifach zu halten. Jetzt denke ich, die Übergabe eines Zeiger auf das Stringobjekt des Parent wäre die beste Lösung. Ich weiss das klingt jetzt albern, dass ich da nicht gleich draufgekommen bin, aber ich bin nunmal nicht fit in C++ Syntax und darf nicht zu viel Zeit in meine Ausbildung stecken, da ich im Erreichen des Projektziels zu langsam bin.Danke für die ausführliche Literaturliste. Soweit ich sie kenne, sind es aber "Lehrbücher" für gutes C++ Design und deshalb zum Nachschlagen weniger geeignet. Für mich, der ich bereits eine anderen Programmiersprache gut beherrsche, wäre ein Nachschlagewerk für C++ Syntax ideal, weil ich schon weiß was ich will, ich weiß nur nicht wie ich es den C++ Compiler sagen kann.
Liebe Grüße
Bernd
-
BerndD schrieb:
Anderseits vertrittst du das andere Extrem, nämlich das der Konstruktorrumpf eigentlich überflüssig sei. Das ist er aber nur, wenn sich das Objekt vollständig über seine ctor-Parameter konfigurieren lässt.
In der Regel ist er das auch, und ich sagte ja im Regelfall benutze ich den Konstruktorrumpf nicht, ich habe nicht gesagt das ich ihn nie verwende. Wobei ich wenn ein Objekt anders "konfiguriert" wird, in der Regel auch einen Anderen Konstruktor habe und damit auch die Initialisierungsliste anders setze.
asc schrieb:
Vielleicht gibt es ein Verständigungsproblem, weil ich als Delphi-Programmierer Begriffe anders verwende. Wenn ich von Parentobjekt spreche, hat das nichts mit Ableiten (in sinne von Vererben) zu tun. Der Parent ist das Objekt, das die Child-Objekte als Membervariablen enthält.
Okay, das ist im OO Jargon eine Komposition. Das Objekt enthält andere Objekte. Ein sinnvoller Begriff wie man die enthaltenen Objekte nennen kann ist mir aber nicht geläufig. Der Begriff Child/Parent wird aber auch mehrdeutig verwendet (Deine Begrifflichkeit erinnert mich an Parentfenster / Childfenster).
asc schrieb:
In C++ sagt man vielleicht Container dazu, obwohl das IMHO nur für Klassen richtig ist, die zur Laufzeit eine unbestimmte Menge vom Objekten aufnehmen kann.
Container können auch eine fixe Anzahl aufnehmen. Es gibt z.B. im C++ TR1 Standard wenn ich mich nicht irre ein Arraytyp.
asc schrieb:
In Delphi hat zum Beispiel jede Klasse eines GUI-Elementes einen "Parent"-Zeiger auf das in der Fensterhirachie übergeordnete Objekt.
Ja leider mehrdeutig, im Zusammenhang den du beschrieben hast klang es nach Parent/Child im OO Sinne ;p
asc schrieb:
Jetzt denke ich, die Übergabe eines Zeiger auf das Stringobjekt des Parent wäre die beste Lösung. Ich weiss das klingt jetzt albern, dass ich da nicht gleich draufgekommen bin, aber ich bin nunmal nicht fit in C++ Syntax und darf nicht zu viel Zeit in meine Ausbildung stecken, da ich im Erreichen des Projektziels zu langsam bin.
Gut bei einem einfachen String wäre es zuviel des Guten, aber ansonsten schreit das für mich nach einem "shared_ptr" (Smartpointer mit Referenzzählung, gibt im Magazin dazu ein Titel, ansonsten sei hier auf die empfehlenswerte boost Bibliothek verwiesen http://www.boost.org/libs/smart_ptr/smart_ptr.htm, wobei der nächste Standard C++0x shared_ptr in die Standardbibliothek übernimmt).
asc schrieb:
Danke für die ausführliche Literaturliste. Soweit ich sie kenne, sind es aber "Lehrbücher" für gutes C++ Design und deshalb zum Nachschlagen weniger geeignet. Für mich, der ich bereits eine anderen Programmiersprache gut beherrsche, wäre ein Nachschlagewerk für C++ Syntax ideal, weil ich schon weiß was ich will, ich weiß nur nicht wie ich es den C++ Compiler sagen kann.
Es sind um genau zu sein Bücher die eher für Fortgeschrittene und Profis gedacht sind, und zeigen wie man effizient mit C++ umgeht, was es für Fallstricke gibt und zudem zeigen sie mit sicherheit Wissenslücken aus. Dennoch empfehle ich die aktuelle deutsche oder englische Ausgabe von Effektiv C++ Programmieren (Scott Meyer).
Als Referenz geeignet, wenn auch nicht unbedingt leicht zu lesen, ist einmal die quasi C++ Bibel, vom C++ Erfinder:
* Bjarne Stroustrup [Addison-Wesley], Die C++-ProgrammierspracheFür spezielle Themen wie Templates, STL etc. gibt es dann ebenso recht gute Bücher.
cu André