Konstruktoren der Member aufrufen OHNE Initialisierungsliste
-
hallo, erstmal danke fuer diene Antwort!!!
Hmm, mein Beispiel war vereinfacht.
meine Klasse A wird mit einer Liste von Pointern initialisiert.Um genau zu sein ist die Signatur des Konstuktors:
A( QList<PointCorrespondence *> & list);
diese liste wird im Kontruktor von B aufgebaut:
B::B() { QList<PointCorrespondence *> list; list.append(new PointCorrespondence(...)); list.append(new PointCorrespondence(...)); list.append(new PointCorrespondence(...)); list.append(new PointCorrespondence(...)); list.append(new PointCorrespondence(...)); list.append(new PointCorrespondence(...)); list.append(new PointCorrespondence(...)); . . . list.append(new PointCorrespondence(...)); // jetzt soll die initialisierung kommen. a = A(list); }Offensichtlich koennte ich es ja auch so realisieren, dass A mit einer leeren Liste konstruiert wird und ich die Listenelemente einfach in das "fertige" A anhaenge:
B::B() : a() { } somewhere else: b.append(new PointCorrspondence(...); ... b.append(new PointCorrspondence(...);ABER:
das Objekt vom typ B ist erst "einsatzbereit" nachdem die Listenelemente uebergeben UND VERARBEITET wurden.
Ich suche einfach nur eine elegante Moeglichkeit dazu.
Und der Copy-Konstruktor muesste eben die gesamte Liste kopieren.Gibt es da nix eleganteres?
Gruesse,
Olli
a
-
Wenn du einen Smart-Pointer nimmst, kannst du den Konstruktionszeitpunkt selbst festlegen:
#include <memory> class B { std::auto_ptr <A> a; B (void) { x = berechneX (); y = berechneY (); a.reset (new A (x, y)); } };
-
Dafür kannst Du z.B. eine statische Initialisierfunktion spendieren:
class A{ public: A(int x, int y); } class B{ public: B(); private: static QList<PointCorrespondence *> makeList(SomeType someArg); A a; } //... QList<PointCorrespondence *> B::makeList(SomeType someArg) { QList<PointCorrespondence *> list; //erstelle Liste return list; } B::B() : A(makeList(args)){}
-
audacia schrieb:
Wenn du einen Smart-Pointer nimmst, kannst du den Konstruktionszeitpunkt selbst festlegen:
#include <memory> class B { std::auto_ptr <A> a; B (void) { x = berechneX (); y = berechneY (); a.reset (new A (x, y)); } };Vielen Dank!
Das hoert sich super an. Aber dumme Frage:
Wenn ich generell auf einen Zeifer auf A anstatt einem echten Member A umsteigen wurde, ware mein Problem ja eh erledigt, oder?
-
olidem schrieb:
Vielen Dank!
Das hoert sich super an.Nein. Dumme Idee.
Nimm Tachyons Variante.
Die ist viel besser.
-
Tachyon schrieb:
Dafür kannst Du z.B. eine statische Initialisierfunktion spendieren:
class A{ public: A(int x, int y); } class B{ public: B(); private: static QList<PointCorrespondence *> makeList(SomeType someArg); A a; } //... QList<PointCorrespondence *> B::makeList(SomeType someArg) { QList<PointCorrespondence *> list; //erstelle Liste return list; } B::B() : A(makeList(args)){}Hallo, auch Dir moechte ich danken!
Generell erkenne ich, dass dieser Vorschlag eigentlich richtig ist, mir aber vieleFreiheitsgrade nimmt. Die Korrespondenzen werde ja irgendwann auch mal dznamisch gefunden werden...
-
Shade Of Mine schrieb:
olidem schrieb:
Vielen Dank!
Das hoert sich super an.Nein. Dumme Idee.
Nimm Tachyons Variante.
Die ist viel besser.Warum?
Wenn ich auf Pointer umsteige:
class B{ B(); private: A* a; } //... B::B(){ x = berechne_x(); y = berechne_y(); a = new A(x,y); }Wo soll der Nachteil sein?
Gruesse,
Olli
-
olidem schrieb:
Hallo, auch Dir moechte ich danken!
Generell erkenne ich, dass dieser Vorschlag eigentlich richtig ist, mir aber vieleFreiheitsgrade nimmt. Die Korrespondenzen werde ja irgendwann auch mal dznamisch gefunden werden...Was meinst Du? Welche Freiheiten werden Dir dadurch genommen?
-
Tachyon schrieb:
olidem schrieb:
Hallo, auch Dir moechte ich danken!
Generell erkenne ich, dass dieser Vorschlag eigentlich richtig ist, mir aber vieleFreiheitsgrade nimmt. Die Korrespondenzen werde ja irgendwann auch mal dznamisch gefunden werden...Was meinst Du? Welche Freiheiten werden Dir dadurch genommen?
Naja,
ich finde es einfach nicht schoen. ich muesste ja alles was im Konstruktor vor der Stelle kommt, an der ich mein Objekt A konstruieren moechte in eine entfernte Funktion auslagern.
Ist einfach nicht uebersichtlich.
Was gibt es gegen die Pointer-Variante zu sagen? Wie ist das mit der Performance?
-
Shade Of Mine schrieb:
Nein. Dumme Idee.
Nimm Tachyons Variante.
Die ist viel besser.Ich glaube, du solltest dir dringend angewöhnen, Herabsetzungen der Äußerungen anderer stante pede zu begründen

Überhaupt wirfst du relativ häufig unbegründete Statements mit Absolutheitsanspruch in den Raum. Wie ich kürzlich bereits erwähnte, bist du mir bei mindestens zweien solcher Statements noch eine Begründung schuldig. Daß du viel Ahnung von der Materie hast, enthebt dich nicht der Verpflichtung, deine Äußerungen zu begründen.olidem schrieb:
Was gibt es gegen die Pointer-Variante zu sagen? Wie ist das mit der Performance?
Dagegen spricht, daß jemand auf die Idee kommen könnte, in einer der Methoden von B
a.reset()aufzurufen, was dein Objekt in einen ungültigen Zustand versetzen würde. Außerdem kann das Auslagern der Listenerstellung durchaus auch zur Übersichtlichkeit beitragen; das hängt vom konkreten Anwendungsfall ab.
-
Der Vorteil bei der Variante mit der Funktion ist, dass Du ein komplett konstruiertes und initialisiertes Objekt A hast, wenn B konstruiert wird. Bei der Variante mit dem Pointer wird erstmal ein leeres oder uninitialisiertes Objekt A erstellt, und das wird dann nachträglich initialisiert. Bei der Variante mit dem scoped_ptr hast Du erst einen uninitialisierten Pointer, der dann nachträglich ins richtige Lot gebogen wird.
audacia schrieb:
Dagegen spricht, daß jemand auf die Idee kommen könnte, in einer der Methoden von B
a.reset()aufzurufen, was dein Objekt in einen ungültigen Zustand versetzen würde.Ne, das ist es nicht.
-
Dafür gibt es ein eigenes Idiom: "Base-from-Member" http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Base-from-Member
Gruß
-
Tachyon schrieb:
Bei der Variante mit dem Pointer wird erstmal ein leeres oder uninitialisiertes Objekt A erstellt, und das wird dann nachträglich initialisiert.
Wo wird da ein leeres Objekt A erstellt?
Tachyon schrieb:
Bei der Variante mit dem scoped_ptr hast Du erst einen uninitialisierten Pointer, der dann nachträglich ins richtige Lot gebogen wird.
Richtig - aber das Problem hat man bei allen seriell erfolgenden Anweisungen mehr oder weniger. Es ist unschön, daher die Auslagerung der Konstruktion von A in eine zusätzliche Methode sinnvoll, aber weiter unproblematisch. Oder übersehe ich etwas?
Und doch, auch das mit .reset() ist ein valider Grund. Alles, was eine Möglichkeit bietet, ein Objekt in einen nicht definierten Zustand zu befördern, ist potentiell gefährlich. Allerdings ist das Risiko hier natürlich überschaubar.
-
audacia schrieb:
olidem schrieb:
Was gibt es gegen die Pointer-Variante zu sagen? Wie ist das mit der Performance?
Dagegen spricht, daß jemand auf die Idee kommen könnte, in einer der Methoden von B
a.reset()aufzurufen, was dein Objekt in einen ungültigen Zustand versetzen würde. Außerdem kann das Auslagern der Listenerstellung durchaus auch zur Übersichtlichkeit beitragen; das hängt vom konkreten Anwendungsfall ab.Der Nachteil ist dass man plötzlich Zeiger hat. Ergo langsames new und dauernd dereferenzieren. weiters ist der op= und copy ctor nicht mehr automatisch generiert korrekt.
und generell hat man um das problem herum designt.
tachyons variante ist dagegen einfach ideal. da gibts nix auszusetzen. berechnung von X und Y muss, wenn es nicht in einem statement geht sowieso in eine eigene funktion wandern... uu ist eine funktion createObject() sogar besser...
jedenfalls ist ein smartpointer eine loesung für ein resource problem (erstellen, kopieren, zerstören von resource). wir haben hier kein solches problem, ergo brauchen wir eine andere lösung.
-
audacia schrieb:
Und doch, auch das mit .reset() ist ein valider Grund. Alles, was eine Möglichkeit bietet, ein Objekt in einen nicht definierten Zustand zu befördern, ist potentiell gefährlich. Allerdings ist das Risiko hier natürlich überschaubar.
vector<string> v;
v.push_back("test");
v.front().~string();*BUMM*
und schon invalider zustand.schau dir mal murphy vs machiavelli an...
-
Shade Of Mine schrieb:
Der Nachteil ist dass man plötzlich Zeiger hat. Ergo langsames new und dauernd dereferenzieren. weiters ist der op= und copy ctor nicht mehr automatisch generiert korrekt.
Ah. Warum nicht gleich?

Shade Of Mine schrieb:
vector<string> v;
v.push_back("test");
v.front().~string();Ich hatte mir überlegt, ob ich darauf eingehen sollte, daß man auch gewöhnliche Member fälschlicherweise destruieren könnte. Ich ließ es bleiben, weil ich mir dachte, daß es wohl abwegig genug sei, um nicht der Erwähnung wert zu sein - der direkte Aufruf eines Destruktors ist nicht minder rohe Gewalt als das Anwenden von reinterpret_cast<>. Hingegen ist das Aufrufen der Methoden eines Smart-Pointers nicht intuitiv abwegig oder ungewöhnlich - beispielsweise wäre folgender Fehler sehr unauffällig:
// falsch void B::swap (B& rhs) { a.swap (rhs.a); } // richtig void B::swap (B& rhs) { a->swap (*rhs.a); }Das sieht wunderbar harmlos aus, verursacht aber seltsame Bugs, sobald z.B. ein anderer Member einen Zeiger auf das Objekt in a hält.
Edit: Typographie, Kleinigkeiten.