Warum kann ich meine Klasse nicht Konstrukteurs-Anruf von einer Instanz dieser Klasse in C ++?

StackOverflow https://stackoverflow.com/questions/2146901

  •  23-09-2019
  •  | 
  •  

Frage

Wenn ein Objekt einer Klasse die destructor dieser Klasse nennen, als ob es sich um eine reguläre Funktion ist? Warum kann es nicht den Konstruktor der gleichen Klasse nennen, als eine seiner regulären Funktionen? Warum der Compiler hindert uns daran, dies zu tun?

Zum Beispiel:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

void main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles
 objC.c() ;  // compilation error
}
War es hilfreich?

Lösung

Per Definition ist ein Konstruktor nur einmal aufgerufen, wenn das Objekt erstellt wird. Wenn Sie den Zugriff auf ein Objekt haben, dann müssen sie erstellt worden sind, so dass Sie nicht erlaubt sind wieder den Konstruktor zu nennen - dies ist der Grund, warum explizite Konstruktoraufrufe nicht erlaubt. In ähnlicher Weise Destruktoren müssen nur einmal aufgerufen werden, wenn das Objekt zerstört wird. Wenn dies immer automatisch erfolgen könnte, dann würde die Sprache auch explizit destructor Anrufe verbieten.

Doch in einigen Fällen könnten Sie eine genaue Kontrolle über die Speicherverwaltung werden soll, und die Fähigkeit, explizit zu erzeugen und zerstören Objekte im Speicher, dass Sie verwalten. Zu diesem Zweck stellt die Sprache „Platzierung neue“ ein Objekt an einem beliebigen Ort zu schaffen, und explizite destructor Anrufe Objekte zu zerstören, auf diese Weise erstellt. Eine explizite Konstruktoraufruf wäre nicht sinnvoll sein, da Sie die Lage des neuen Objekts angeben müssen in der Lage sein - so dass Sie „Platzierung neue“ statt zu bekommen. Eine explizite Destruktoraufrufs ausreichend ist, so gibt es keine Notwendigkeit, eine Art matching „Platzierung löschen“.

zu erfinden

Also: Es gibt keine gültige Verwendung für explizite Konstruktoraufrufe, so dass sie nicht erlaubt sind. Es ist eine gültige Verwendung für explizite destructor Anrufe, so dass sie (syntaktisch) erlaubt, mit der Regel, dass Sie immer nur sie auf Objekte verwenden müssen, die nicht auf andere Weise zerstört werden, dh Objekte erstellt mit „Platzierung neu“, und dass Fall nennen sie genau einmal. Mit ihnen in irgendeiner anderen Art und Weise, wie so viele C ++ Fehler, kompilieren aber nicht definiertes Verhalten geben.

Andere Tipps

Ich glaube, Sie explizit den destructor anrufen, wenn Sie sicherstellen, dass die Instanz ersetzt / neu mit einem Aufruf an der Platzierung neu:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

int main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles
 new (&objC) c;  // placement new invokes constructor for the given memory region
}

Ich habe dies in der Praxis nie gesehen, aber logisch sollte es funktionieren (es sei denn, c Konstruktor werfen kann, in diesem Fall, denke ich, könnte die Hölle losbrechen während Stack Unwinding).

Doch was Sie wollen wahrscheinlich nur Zuordnung:

objC = c();

Wenn der destructor hat Nebenwirkungen, die Sie interessiert sind, implementieren Zuordnung mit dem Copy-and-Swap-Idiom, das das destructor für den „linken“ Wert aufgerufen wird.

Ein destructor muss auf einer vorhandenen Instanz einer Klasse aufgerufen werden - die Instanz zerstörenden ist, was es tut. Ein Konstruktor erstellt eine brandneue Instanz einer Klasse, so auf einer vorhandenen Instanz Aufruf macht keinen Sinn.

Dies ist vergleichbar mit der Art und Weise neu und löscht Arbeit:

int * p = new int;    // call to new needs no existing instance
delete p;             // call to delete requires existing instance

Und Notiz in Ihrem Code, würde das Objekt zweimal zerstört werden, einmal explizit und implizit einmal am Ende seines umschließenden Umfangs. Sie in der Regel nur explizit eine destructor anrufen, wenn Sie etwas Ungewöhnliches tun werden, wahrscheinlich die Verwendung der Platzierung neuer beteiligt ist.

Wenn Sie wirklich so etwas wie dies tun müssen, um, erstellen Sie einfach eine zusätzliche Funktion und rufen Sie es von außen und von den Konstruktor selbst, aber mal sehen, was passiert, wenn Sie einen solchen Anruf benötigen Sie:

#include<new>

class A
{
//members
};

int main()
{
//allocate buffer
char* buffer = new char[sizeof(A)];
//construct A on that memory space
A * ptrToA = ::new (buffer) A();
//destroy the object
ptrToA->~A();
//deallocate the buffer
delete[] buffer;
}

Ein Beispiel, wo Sie Platzierung neue Nutzung finden, ist der Standard Behälter. Der allocator nimmt in der Verantwortung der zuzuteilen Puffer (zuteilen Element) und die Objekte sind so konstruiert, dass über Puffer, wie sie in den Behälter hinzugefügt werden. Zum Beispiel, wenn Sie das tun Reserve auf Vektorobjekt, behält es den Raum für N Bedeutung Objekte zuordnet Raum für N-Objekte sie aber nicht konstruieren. Dann wenn Sie push_back etc, Elemente hinzuzufügen, werden sie über erstellt, dass Puffer. Im Grunde ist es eine Technik, den Overhead zu reduzieren, wiederholte Anrufe Speicherzuordnungsfunktion. Und dann, wenn Sie fertig, die Vektor destructor würden die Objekte Aufruf der destructor zerstören ausdrücklich für alle Objekte in es und dann das Freisetzen () aufrufen Funktion der Zuordner den Speicher freizugeben. Hoffe, das hilft.

Probieren Sie dies aus:

obj.ClassName :: Classname (); // funktioniert es in VC 6.0 Compiler

Eine weitere Möglichkeit, um die Einschränkung zu denken, dass ein Konstruktor ist nicht nur eine andere Funktion. Betrachten wir seine Definition: Im Gegensatz zu anderen Funktionen, es keinen Rückgabewert hat, und es kann eine Initialisiererliste haben. Dass es gerade geschieht, die meisten der Syntax einer Funktion Art ist ein Zufall zu haben; es existiert wirklich nur zu dem Zweck eine neue Instanz eines Objekts initialisiert wird.

Der Konstruktor gibt es "c ()" verwendet, um mit neuen, d.h.

c objC = new c();

Wenn Sie Ihren Konstruktor außerhalb des eigentlichen Bau der Klasseninstanz anrufen mögen, dann haben Sie entweder nicht verstanden, den Zweck des Konstruktor oder versuchen Funktionalität zu setzen dort, dass nicht da sein sollte.

Sie fragen nach einem bestimmten Stil der Syntax mehr als eine Sprache Einschränkung. C ++ ermöglicht es Ihnen, ein Objekt der Konstruktor oder Destruktor zu nennen.

c objC;
objC.~c();  // force objC's destructor to run
new(&objC); // force objC's constructor to run

Warum haben die Sprachdesigner nicht diese Syntax verwenden statt?

c objC;
delete(&objC) c; // force objC's destructor to run
new(&objC) c;    // force objC's constructor to run

Oder warum nicht:

c objC
objC.~c(); // force objC's destructor to run
objC.c();  // force objC's constructor to run

Meine Meinung darüber, warum sie wählten die Syntax sie taten: Die erste Alternative ist flexibler als die zweite. Ich kann den Konstruktor / destructor an jeder Adresse rufen nicht nur eine Instanz. Diese Flexibilität ist für die Zuteilung notwendig, aber nicht für die Zerstörung. Auch die erste Option des Lösch scheint sehr gefährlich zu sein, wenn virtuelle Destruktoren in das Chaos zu bekommen.

Sie können den Konstruktor einer Instanz der Klasse mit typeof aufrufen:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

void main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles (but is a bad idea)
 typeof objC otherObjC;  // so does this.
}

Das nicht den Wert der Instanz objC beeinflussen, sondern erstellt eine neue Instanz otherObjC objC Klasse Konstruktor.

. Hinweis: dies etwas tun können Sie nicht erwarten, wenn der statische Typ eine Basisklasse des dynamischen Typ der Instanz, die Sie haben

Wer sagt, dass Sie nicht können? Sie müssen nur wissen, wie.

void Foo::Bar() {
  *this = Foo(); // Reset *this
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top