Perché non posso chiamare il costruttore della mia classe da un'istanza di quella classe in C ++?

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

  •  23-09-2019
  •  | 
  •  

Domanda

Quando si può un oggetto di una classe chiamare il distruttore di quella classe, come se fosse una funzione regolare? Perché non può chiamare il costruttore della stessa classe, come una delle sue funzioni regolari? Perché il compilatore ci impedisce di fare questo?

Ad esempio:

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

void main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles
 objC.c() ;  // compilation error
}
È stato utile?

Soluzione

Per definizione, un costruttore viene chiamato solo una volta, quando si crea l'oggetto. Se si ha accesso a un oggetto, allora deve essere stato creato, quindi non ti è permesso di chiamare di nuovo il costruttore - questo è il motivo per cui le chiamate costruttore esplicito non sono ammessi. Analogamente, distruttori essere richiamati solo una volta, quando l'oggetto viene distrutto. Se questo potrebbe sempre fatto automaticamente, allora la lingua sarebbe anche vietare le chiamate distruttore esplicite.

Tuttavia, in alcune circostanze, si potrebbe desiderare un controllo preciso sulla gestione della memoria, e la possibilità di creare in modo esplicito e distruggere gli oggetti all'interno della memoria che si sta gestendo. A tal fine, il linguaggio offre "nuova collocazione" per creare un oggetto in una posizione arbitraria, e distruttore esplicito chiama a distruggere gli oggetti creati in questo modo. Una chiamata costruttore esplicito non sarebbe utile, in quanto è necessario essere in grado di specificare la posizione del nuovo oggetto - in modo da ottenere "nuova collocazione", invece. Una chiamata distruttore esplicito è sufficiente, quindi non c'è bisogno di inventare una sorta di corrispondenza "placement delete".

Quindi: non c'è uso valida per le chiamate costruttore esplicito, in modo che non sono ammessi. C'è un uso valida per le chiamate distruttore esplicite, in modo che siano (sintatticamente) ha permesso, con la regola che si deve sempre e solo utilizzarli su oggetti che non saranno altrimenti essere distrutti, vale a dire oggetti creati utilizzando "nuova collocazione", e in quel tra maiuscole e li chiamano esattamente una volta. Il loro utilizzo in qualsiasi altro modo, come tanti errori C ++, compilerà ma darà un comportamento indefinito.

Altri suggerimenti

Penso che si può chiamare in modo esplicito il distruttore se si assicurarsi che l'istanza viene sostituita / ricreato con una chiamata a nuova collocazione:

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
}

Non ho mai visto questo, in pratica, ma logicamente dovrebbe funzionare (a meno che il costruttore di c può buttare, nel qual caso, immagino, l'inferno potrebbe cadere in pila di svolgimento).

Tuttavia, ciò che probabilmente si desidera è proprio incarico:

objC = c();

Se il distruttore ha effetti collaterali che si sono interessati a, implementare l'assegnazione utilizzando l'idioma di copia e-swap che ottiene il distruttore invocato per il valore "sinistra".

Un distruttore deve essere chiamato su un'istanza esistente di una classe - distruggendo l'istanza è ciò che fa. Un costruttore crea una nuova istanza di una classe, in modo da chiamare su un'istanza esistente non ha senso.

Questo è simile al modo in cui nuovi e cancellare il lavoro:

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

E nota nel codice, l'oggetto sarebbe stato distrutto due volte, una volta in modo esplicito, e una volta implicitamente alla fine del suo campo di applicazione racchiude. In genere solo chiamare esplicitamente un distruttore se si sta facendo qualcosa di insolito, probabilmente coinvolge l'uso di nuova collocazione.

Se avete veramente bisogno di fare qualcosa di simile, basta creare una funzione aggiuntiva e chiamare da fuori e dal costruttore stesso, ma vediamo che cosa succede quando si ha bisogno di una chiamata:

#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;
}

Un esempio dove si possono trovare nuova collocazione utilizzo è lo standard contenitori. L'allocatore assume la responsabilità di assegnare il tampone (allocare membro) e gli oggetti vengono costruiti su quella tampone quando vengono aggiunti nel recipiente. Ad esempio, quando si fa Riserva su oggetto vettoriale, si riserva lo spazio per N oggetti che significa alloca spazio per N oggetti ma non li costruire. Poi quando non push_back ecc, per aggiungere elementi, sono stati creati oltre che buffer. Fondamentalmente, è una tecnica per ridurre il sovraccarico di ripetute chiamate funzione di allocazione di memoria. E poi una volta fatto, il vettore distruttore distruggerebbe gli oggetti chiamando il distruttore esplicitamente per tutti gli oggetti in essa e quindi chiamare il deallocare () funzione della allocatore per rilasciare la memoria. Spero che questo aiuti.

Prova questo fuori:

obj.ClassName :: ClassName (); // funziona in VC 6.0 compilatore

Un altro modo di pensare la restrizione è che un costruttore è non solo un'altra funzione. Prendere in considerazione la sua definizione: a differenza di altre funzioni, non ha alcun valore di ritorno, e può avere una lista di inizializzazione. Che guarda caso ha la maggior parte della sintassi di una funzione è sorta di una coincidenza; esiste davvero solo allo scopo di inizializzare una nuova istanza di un oggetto.

Il costruttore c'è "c ()" usato con nuovo, cioè

c objC = new c();

Se si desidera chiamare il tuo costruttore al di fuori della costruzione vera e propria del l'istanza della classe si ha o non hai capito lo scopo del costruttore o stanno cercando di mettere funzionalità in là che non dovrebbe essere lì.

Si sta chiedendo informazioni su un particolare stile di sintassi più di una limitazione della lingua. C ++ consente di chiamare il costruttore o distruttore di un oggetto.

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

Perché i progettisti lingua non usare questa sintassi invece?

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

Oppure, perché non:

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

La mia opinione sul motivo per cui hanno scelto la sintassi che hanno fatto: La prima alternativa è più flessibile rispetto alla seconda. Posso chiamare il costruttore / distruttore su qualsiasi indirizzo non solo un esempio. Che la flessibilità è necessaria per l'allocazione, ma non per la distruzione. Anche il primo eliminazione opzione sembra essere molto pericoloso una volta distruttori virtuali entrare in mensa.

è possibile richiamare il costruttore di un'istanza di classe utilizzando typeof:

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.
}

Ciò non pregiudica il valore dell'istanza objC, ma crea una nuova istanza di otherObjC usando costruttore della classe di objC.

. Nota: questo può fare qualcosa che non ti aspetti se il tipo statico è un baseclass di tipo dinamico dell'istanza avete

Chi dice che non si può? Devi solo sapere come.

void Foo::Bar() {
  *this = Foo(); // Reset *this
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top