2008-11-24 5 views

Antwort

14

Sie Platzierung neu verwenden können: http://en.wikipedia.org/wiki/Placement_new die

new (&instance) A(2); 

jedoch erlaubt, von Ihrem Beispiel würden Sie zweimal einen Konstruktor auf ein Objekt anrufen, die sehr schlechte Praxis. Stattdessen würde ich empfehlen Sie tun nur

A instance(2); 

neue Platzierung in der Regel nur verwendet, wenn Sie den Speicher auf preallocate benötigen z.B. in einem benutzerdefinierten Speichermanager.

+0

@Michael: was hat es damit zu tun? Es ist ein Low-Level-Sache ... und ein neues Objekt wird sowieso erstellt werden, nur über das alte, bin ich falsch? – badbadboy

+0

Hoffe, meine Bearbeitung macht es klarer. –

+0

Ich habe diese Antwort +1 gegeben - aber ich stimme nicht mit dem Punkt über die Verwendung in dem speziellen Fall der Vorbelegung von Speicher überein. In diesem Fall ist das 'Instanz' -Objekt * * der vorbelegte Speicher. –

2

Ich bin mir ziemlich sicher, dass Sie das nicht tun können. Das ist der springende Punkt, Konstruktor IS erstellt eine Instanz der Klasse.

Wird ein Konstruktor gar nicht oder zweimal aufgerufen - welche Konsequenzen hätte er?

Was Sie natürlich tun könnten, ist extrahieren einige Konstruktor Logik in die Methode, und ruft diese Methode sowohl im Konstruktor und nach der Erstellung des Objekts.

+0

rechts - Sie sollten nicht in der Lage sein, einen Konstruktor 'direkt' aufzurufen, außer in @ Michaels Methode der "Platzierung neu" - aber selbst das ist nichts, was Sie normalerweise sehen werden :) – warren

10

Nr

ein Verfahren für das Set erstellen und aus dem Konstruktor aufrufen. Diese Methode steht dann auch für später zur Verfügung.

class A{ 
    A(int a) { Set(a); } 
    void Set(int a) { } 
} 

A instance; 

instance.Set(2); 

Sie werden wahrscheinlich auch einen Standardwert oder einen Standardkonstruktor benötigen.

+0

Es ist möglich - Platzierung Neu macht genau das, was der OP wollte. –

+3

Stimmt, aber auch wenn OP darum gebeten wurde, ist es fast genau das Richtige. –

4

Kein

Calling instance.A() or A(1) is seens as casting 'function-style cast' : illegal as right side of '.' operator 

Normalerweise, wenn eine Funktion/Funktionalität in Konstruktor erforderlich ist als auch nach Aufgabe wird construted es in init() Methode gesetzt wird und auch in Konstruktor und an anderer Stelle verwendet.

Beispiel:

class A{ 
     A(int a) 
     { 
     init(a); 
     } 

    void init(int a) { } 
    } 

     A instance; 

     instance.init(2); 
-1

Gerade Zusammenfassend sind die drei Möglichkeiten, den expliziten Konstruktor sind über

  1. A Instanz (2) zu spezifizieren; // tut A instance = 2; jemals arbeiten?

  2. A * Instanz = neu A (2); // nie sicher über & versus * hier, ich selbst

  3. neu (& Instanz) A (2);

und Aromen von denen. Die Idee Ziel ist es, zu arrangieren, dass zu keinem Zeitpunkt ein Objekt erstellt wird, das nicht in einem richtigen initialisierten Zustand ist, und Konstruktoren sollen das sicherstellen. (Das bedeutet, dass Methoden nicht prüfen müssen, ob eine .init (...) Methode erfolgreich aufgerufen wurde oder nicht.)

Dies scheint mir der funktionalere Weg, um dies zu tun, vor allem für Klassen, die Teile von Frameworks sind und in Bibliotheken wiederverwendet werden. Wenn Sie daran interessiert sind, arbeiten Sie daran, dass alle Konstruktoren, einschließlich der Standardkonstruktoren, eine voll funktionsfähige Instanz bereitstellen.

Ausnahmefälle: Es gibt Dinge, die Sie möglicherweise nicht in der Konstruktoroperation haben, wenn es möglich ist, dass sie fehlschlagen, es sei denn, es ist angemessen, eine Ausnahme vom Konstruktor zu werfen. Und manche Leute haben gerne "leere" Instanzen, die mit nachfolgenden Methoden propagiert werden und sogar Initialisierungs-Mitglieder. Es ist interessant, Möglichkeiten zu finden, um solche Situationen zu mildern, und robuste Instanzen zu haben, die keine schlechten Zustände haben, gegen die bei Methodenimplementierungen und bei der Verwendung geschützt werden muss.

PS: In einigen komplexen Fällen kann es nützlich sein, eine initialisierte Instanz (Referenz) als Ergebnis einer Funktion oder einer Methode in einer "Factory" -Klasse zu liefern, so dass das Intermediate, Under-Setup Instanz wird nie außerhalb der Kapselung der Factory-Klasseninstanz oder -funktion gesehen. Das gibt uns

+4. Eine * Instanz = MakeAnA (2);

+5. Eine * Instanz = InterestingClass.A (2);

+0

Naah, das ist grundsätzlich falsch. Betrachte deinen Fall 2, dieses Objekt * kann * tatsächlich in einem nicht initialisierten Zustand sein, so dass die Überprüfung durchgeführt werden muss (und ja, du brauchst hier ein '*' anstelle eines '&', da es ein Zeiger ist). Was das Auslösen von Ausnahmen von Konstruktoren betrifft, ist * dies * angebracht und muss behandelt werden. –

+0

Kann sich das Objekt in (2) in einem nicht initialisierten Zustand befinden, ist dann in (1) und (3) nicht dasselbe Problem möglich? Ich würde annehmen, dass der einzige Weg (2) fehlschlägt (angenommen, dass der Konstruktor dafür ausgelegt ist, einen guten Job zu machen), wenn die Speicherzuweisung fehlschlägt, und das macht instance == NULL. – orcmid

1

Übrigens klingt das wie ein Designfehler. Sobald ein Objekt konstruiert ist, sollte es niemals notwendig sein, es neu zu konstruieren. Eine solche Wiederverwendung von Variablennamen macht den Code eher schwierig zu verstehen. Aus diesem Grund ist es oft falsch (aber manchmal unvermeidbar), herstellerspezifische Funktionen durch eine Zusatzfunktion init oder set verfügbar zu machen.

Wie Michael sagte, könnte Platzierung neu hier verwendet werden, ist aber wirklich für verschiedene Zwecke gedacht. Auch bevor ein neues Objekt in einem Speicherplatz konstruieren, müssen Sie explizit das alte Objekt zerstören:

instance.~A(); 

Auch placement new eine abgeneigt Wirkung auf Ihrem Gedächtnis haben kann, weil Überlastung könnte erwarten, dass der Speicher geleitet wird gehört zum Haufen! Abschließend: nicht. machen. Dies.

EDIT Um zu zeigen, dass wirklich notwendig (für Nicht-POD) die destructor ruft, sollten Sie die folgenden Beispielcode:

#include <iostream> 

struct A { 
    A(int a) { std::cerr << "cons " << a << std::endl; } 
    ~A() { std::cerr << "dest" << std::endl; } 
}; 

int main() { 
    A instance(2); 
    new (&instance) A(3); 
} 

Da die Programmergebnisse in der folgenden Ausgabe erwartet:

cons 2 
cons 3 
dest 

... was bedeutet, daß der Destruktor für das erste Objekt ist nicht genannt. Dasselbe gilt für alle Ressourcen, die A möglicherweise erworben haben.

+0

konrad, müssen Sie nicht: "Für ein Objekt eines Klassentyps mit einem nicht-trivialen Destruktor muss das Programm den Destruktor nicht explizit aufrufen, bevor der Speicher, den das Objekt belegt, wiederverwendet oder freigegeben wird". Ob dies auch für Objekte mit trivialen Faktoren gilt oder nicht, ich weiß nicht –

+0

ich würde vermuten, dass es wahr ist für diejenigen, die wahr sind. Wäre nicht sinnvoll, das dort zu verbieten, aber erlaube das für Nicht-Triviales –

+0

@litb, bitte ausführlicher. Ich nehme an, dass Sie eine Passage missverstehen, die sich nicht auf expliziten Konstruktoraufruf bezieht, da der Destruktor definitiv nicht automatisch aufgerufen wird, sondern manuell aufgerufen werden muss (siehe Update zu meinem Beitrag). –

0

Nein, das geht nicht. Die einzige Möglichkeit, einen Konstruktor aufzurufen, ist das Schlüsselwort "new".