Warum eine Initialisierungsmethode anstelle eines Konstruktors verwenden?
Ich habe in eine neue Firma und ein Großteil der code-Basis verwendet Initialisierungs-Methoden statt Konstruktoren.
struct MyFancyClass : theUberClass
{
MyFancyClass();
~MyFancyClass();
resultType initMyFancyClass(fancyArgument arg1, classyArgument arg2,
redundantArgument arg3=TODO);
//several fancy methods...
};
Sie sagte mir, dass dies etwas damit zu tun gehabt mit dem timing. Dass einige Dinge müssen getan werden nach Bau würde nicht in den Konstruktor. Aber die meisten Konstruktoren sind leer und ich habe nicht wirklich einen Grund für die Verwendung von Konstruktoren.
So, ich Wende mich an Sie, oh Zauberer des C++: warum würden Sie eine init-Methode statt Konstruktor?
InformationsquelleAutor der Frage bastibe | 2010-09-24
Du musst angemeldet sein, um einen Kommentar abzugeben.
Da Sie sagen, "timing", denke ich, es ist, weil Sie wollen, dass Ihre init-Funktionen aufrufen zu können virtuelle Funktionen auf das Objekt. Das funktioniert nicht immer in einem Konstruktor, da im Konstruktor der Basisklasse die abgeleitete Klasse einen Teil des Objekts "noch nicht vorhanden", und insbesondere können Sie nicht den Zugriff auf virtuelle Funktionen definiert, die in der abgeleiteten Klasse. Anstatt die Basisklasse version der Funktion aufgerufen wird, falls definiert. Wenn es nicht definiert ist, (was bedeutet, dass die Funktion ist rein virtuell), Sie bekommen zu undefiniertem Verhalten.
Die anderen häufigste Grund für die init-Funktionen, ist der Wunsch zu vermeiden, Ausnahmen, aber das ist eine ziemlich old-school-Programmier-Stil (und ob es eine gute Idee ist eine ganze argument von seinen eigenen). Es hat nichts zu tun mit Dingen, die nicht funktionieren kann in einem Konstruktor, eher mit der Tatsache zu tun, dass Konstruktoren können nicht zurückgegeben, ist ein Fehler Wert, wenn etwas misslingt. So weit, dass Ihre Kolleginnen und Kollegen gegeben haben, die wirkliche Gründe, ich vermute, das ist es nicht.
InformationsquelleAutor der Antwort Steve Jessop
Ja ich kann mir denken, einige, aber im Allgemeinen ist es nicht eine gute Idee.
Meistens der Grund dafür aufgerufen wird, ist, dass Sie nur Fehler durch exceptions im Konstruktor (das ist wahr) in der Erwägung, dass mit einer klassischen Methode können Sie die Rückgabe ein Fehlercode.
Jedoch in richtig entworfenen OO-code der Konstruktor ist verantwortlich für die Festlegung der Klasse von Invarianten. Indem Sie einen Standard-Konstruktor, können Sie eine leere Klasse, damit Sie ändern müssen, um die Invarianten, so dass angenommen wird, sowohl die "null" - Klasse und die "sinnvolle" Klasse... und jede Verwendung der Klasse muss vergewissern Sie sich zunächst, dass das Objekt wurde richtig gebaut ist... es ist krass.
So, jetzt lasst uns entlarven, die "Gründe":
virtual
- Methode: verwenden Sie die Virtual Constructor idiom.assert
am Anfang jeder public-Methode, um sicherzustellen, dass das Objekt nutzbar ist, bevor Sie versuchen, es zu benutzen.operator=
(und implementieren es mit der copy und swap-idiom, wenn der compiler-version nicht Ihren Bedarf anpassen).Wie gesagt, im Allgemeinen, schlechte Idee. Wenn Sie wirklich wollen, zu haben, "die leere" - Konstruktor, machen Sie
private
- und nutzen-generator-Methoden. Es ist so effizient mit NRVO... und Sie können zurückboost::optional<FancyObject>
im Falle der Bau gescheitert.InformationsquelleAutor der Antwort Matthieu M.
Andere gelistet haben viele mögliche Gründe (und die passenden Erklärungen, warum sich die meisten von diesen sind in der Regel nicht eine gute Idee). Lassen Sie mich post ein Beispiel eine (mehr oder weniger) gültig Benutzung von init-Methoden, die eigentlich zu tun hat mit timing.
In einem früheren Projekt hatten wir eine Menge von Service-Klassen und Objekte, von denen jede einen Teil einer Hierarchie, und Querverweise, die einander in verschiedener Weise. Also in der Regel für die Schaffung einer ServiceA, Sie brauchte ein Eltern-service-Objekt, die wiederum benötigt ein service-container, die bereits abhängig von der Anwesenheit von bestimmten Dienstleistungen (ggf. einschließlich ServiceA selbst) bei der Initialisierung Zeit. Der Grund dafür war, dass während der Initialisierung, die meisten der Dienste registriert, sich mit anderen Diensten als Zuhörer zu bestimmten Ereignissen und/oder informiert andere Dienste über das Ereignis der erfolgreichen Initialisierung. Wenn der andere Dienst nicht vorhanden zum Zeitpunkt der Meldung, die Registrierung ist nicht geschehen, somit ist dieser service würde nicht erhalten Sie wichtige Nachrichten später, während der Nutzung der Anwendung. Um brechen Sie die Kette von zyklischen Abhängigkeitenmussten wir die explizite Initialisierung Methoden getrennt von Konstruktoren, die so effektiv Globale service-Initialisierung ein zwei-Phasen-Prozess.
So, obwohl dieses idiom sollte nicht gefolgt werden, im Allgemeinen, IMHO hat es einige gültige verwendet. Allerdings ist es am besten, die Begrenzung der Nutzung auf das minimum, mit Konstruktoren, Wann immer möglich. In unserem Fall war es ein legacy-Projekt, und wir nicht noch völlig verstehen, Ihre Architektur. Zumindest die Verwendung von init-Methoden beschränkt sich auf die service-Klassen - regelmäßige Klassen initialisiert wurden über Konstruktoren. Ich glaube, es könnte ein Weg sein, um umgestalten, dass Architektur beseitigt die Notwendigkeit für service-init-Methoden, aber zumindest ich konnte nicht sehen, wie es zu tun (und um ehrlich zu sein, hatten wir dringendere Probleme zu behandeln zu der Zeit war ich Teil des Projekts).
InformationsquelleAutor der Antwort Péter Török
Zwei Gründe die ich mir denken kann aus der Spitze von meinem Kopf:
InformationsquelleAutor der Antwort gspr
Eine weitere Verwendung solcher Initialisierung im Objekt-pool. Im Grunde verlangen Sie einfach das Objekt aus dem pool. Der pool wird bereits einige N Objekte erstellt, die leer sind. Es ist der Anrufer nun, das kann jede Methode aufrufen, die er/Sie mag um die Mitglieder. Sobald die Anrufer getan hat, mit dem Objekt, es wird Ihnen sagen, den pool zu destory. Der Vorteil ist, bis das Objekt verwendet wird, den Speicher gespeichert werden, und der Anrufer kann über eigene geeignete member-Methode initialisiert das Objekt. Ein Objekt kann serviert eine Menge dazu, sondern der Anrufer muss sich nicht unbedingt alle und auch nicht nötig, initialisieren Sie alle Mitglied der Objekte.
In der Regel denke, der Datenbank-verbindungen. Ein pool haben kann, Haufen von connection-Objekt, und der Anrufer kann sich füllen, die Benutzername, Passwort etc. an.
InformationsquelleAutor der Antwort Manoj R
init () - Funktion ist gut, wenn dein compiler nicht unterstützen Ausnahmen, oder Ihre Ziel-Anwendung kann ein heap (Ausnahme sind in der Regel umgesetzt in einem heap zu erstellen und zu zerstören).
init () - Routinen sind auch nützlich, wenn die Reihenfolge der Konstruktion definiert werden muss. Das heißt, wenn Sie Global Objekte zuweisen, die Reihenfolge, in der der Konstruktor aufgerufen wird, ist nicht definiert. Zum Beispiel:
Standard bietet keine Garantie, dass must_construct_before_instance1's Konstruktor wird aufgerufen, bevor instance1's Konstruktor. Wenn es gebunden ist an hardware, Ordnung in die Dinge zu initialisieren, die entscheidend sein können.
InformationsquelleAutor der Antwort TRISAbits
Und auch ich mag befestigen Sie ein code-Beispiel zu Antwort #1 --
Da auch die msdn sagt :
Beispiel :
Das folgende Beispiel veranschaulicht die Wirkung der Verletzung dieser Regel. Die test-Anwendung erstellt eine Instanz der DerivedType, die Ursachen Ihrer Basisklasse (BadlyConstructedType) - Konstruktor ausgeführt. BadlyConstructedType Konstruktor falsch ruft die virtuelle Methode DoSomething. Wie die Ausgabe zeigt, DerivedType.DoSomething() ausgeführt wird, und tut so, bevor DerivedType Konstruktor ausgeführt wird.
Ausgabe :
Berufung base ctor.
Abgeleitet DoSomething aufgerufen, initialisiert ? Keine
Berufung abgeleiteten ctor.
InformationsquelleAutor der Antwort Tarik
Eher ein Sonderfall: Wenn Sie einen Zuhörer, möchten Sie vielleicht, um es sich selbst irgendwo (wie bei einem singleton oder GUI). Wenn Sie das tun, während der Konstruktor, es Lecks einen Zeiger/Referenz auf sich selbst, das ist noch nicht sicher, da der Konstruktor noch nicht abgeschlossen ist (und möglicherweise sogar völlig Versagen).
Übernehmen die singleton, sammelt alle Hörer und sendet diese Ereignisse, wenn Dinge passieren, empfängt und Falle, und dann in einer Schleife durch die Liste der Listener (einer von Ihnen ist die Instanz, die wir hier reden), um Sie zu senden jede Nachricht. Aber diese Instanz ist noch mitten in seinem Konstruktor, so kann der Anruf fehlschlagen, in alle Arten des bösen. In diesem Fall macht es Sinn, um die Registrierung in eine separate Funktion, die Sie offensichtlich tun nicht Aufruf vom Konstruktor selbst (das würde Niederlage der Zweck vollkommen), sondern von dem übergeordneten Objekt, nach dem Bau abgeschlossen ist.
Aber das ist ein spezieller Fall, nicht die Allgemeine.
InformationsquelleAutor der Antwort Kajetan Abt
Es ist hilfreich beim Ressourcen-management. Sagen Sie, Sie haben Klassen mit Destruktor automatisch freigeben von Ressourcen, wenn das Objekt die Lebenszeit vorbei ist. Angenommen, Sie haben eine Klasse, hält diese Ressource-Klassen, und initiieren Sie Sie in den Konstruktor dieser oberen Klasse. Was passiert, wenn Sie den Zuweisungsoperator zu initiieren, diese höhere Klasse? Nachdem die Inhalte kopiert, die alten höheren Klasse wird aus dem Zusammenhang, und die Destruktoren aufgerufen werden, für alle die resource-Klassen. Wenn diese Ressource-Klassen-Zeiger, die kopiert wurden im Rahmen der Zuordnung, dann alle diese Verweise werden nun ungültige Zeiger. Wenn Sie stattdessen initiieren Sie die Ressource-Klassen in ein separates init-Funktion in der höheren Klasse, Sie vollständig zu umgehen, wird die Ressource-Klasse' Destruktor nie aufgerufen wird, da der Zuweisungsoperator nie hat das erstellen und löschen von diesen Klassen. Ich glaube, das ist was gemeint war von den "timing" - Anforderung.
InformationsquelleAutor der Antwort Christopher
Verwenden Sie eine Initialisierungs-Methode statt Konstruktor, wenn die initialiser muss aufgerufen werden, NACHDEM die Klasse erstellt wurde. Also, wenn die Klasse A wurde als:
und die initisalizer der Klasse A erforderlich, die ein gesetzt werden, dann müssen Sie natürlich so etwas wie:
InformationsquelleAutor der Antwort Alexander Rafferty