2010-03-27 17 views
6

Volevo eseguire 1.000 iterazioni di un programma, quindi impostare un contatore per 1000 in main. Avevo bisogno di reinizializzare varie variabili dopo ogni iterazione, e dato che il costruttore della classe aveva già tutte le inizializzazioni scritte - ho deciso di chiamarlo dopo ogni iterazione, con il risultato di ogni iterazione memorizzata in una variabile in main.Chiamare un costruttore per reinizializzare le variabili non sembra funzionare?

Tuttavia, quando ho chiamato il costruttore, non ha avuto alcun effetto ... mi ci è voluto un po 'per capire - ma non ha reinizializzato nulla!

Ho creato una funzione esattamente come il costruttore, quindi l'oggetto avrebbe una propria versione. Quando l'ho chiamato, ha reinizializzato tutto come mi aspettavo.

int main() 
{ 
Class MyClass() 

int counter = 0; 

while (counter < 1000) 
{ stuff happens } 

Class(); // This is how I tried to call the constructor initially. 
      // After doing some reading here, I tried: 
      // Class::Class(); 
      // - but that didn't work either 
/* Later I used... 
MyClass.function_like_my_constructor; // this worked perfectly 
*/ 
} 

... Qualcuno potrebbe cercare di spiegare il motivo per cui quello che ho fatto era sbagliato, o non ha funzionato, o era sciocco o quello che hai? Voglio dire - mentalmente, ho appena immaginato - merda, posso chiamare questo costruttore e avere tutte queste cose reinizializzate. I costruttori (idealmente) vengono chiamati SOLO quando viene creato un oggetto?

+0

Sembra possibile con * posizionamento nuovo *. http://stackoverflow.com/questions/6868363/how-to-recall-a-constructor-of-an-initialised-object – Eonil

+0

'MyClass = Class();'? –

risposta

8

La linea Class(); chiama il costruttore della classe Class, ma la chiama per creare un "oggetto temporaneo". Dal momento che non usi quell'oggetto temporaneo, la linea non ha alcun effetto utile.

Gli oggetti temporanei (di solito) scompaiono alla fine dell'espressione in cui appaiono. Sono utili per passare come parametri di funzione o per inizializzare altri oggetti. Non è quasi mai utile crearne uno solo in una dichiarazione. Il linguaggio lo consente come espressione valida, è solo che per la maggior parte delle classi non fa molto.

In C++ non è possibile chiamare un costruttore su un oggetto che è già stato creato. Il ciclo di vita di un oggetto C++ è una costruzione e una distruzione. È così che funziona. Se vuoi reimpostare un oggetto durante la sua vita, hai fatto la cosa giusta, ovvero chiamare una funzione per resettarla. A seconda della classe, potrebbe non essere necessario scriverne una: l'operatore di assegnazione predefinito potrebbe fare esattamente ciò di cui hai bisogno. Questo è quando un temporaneo può tornare utile:

Class myObject; 
// ... do some stuff to myObject ... 

myObject = Class(); 

Questo aggiorna myObject con i valori della temporanea appena costruito. Non è necessariamente il codice più efficiente possibile, poiché crea un temporaneo, quindi copia, quindi distrugge il temporaneo, anziché impostare semplicemente i campi sui loro valori iniziali. Ma a meno che la tua classe non sia enorme, è improbabile che fare tutto questo 1000 volte richieda una quantità notevole di tempo.

Un'altra opzione è solo quello di utilizzare un nuovo oggetto per ogni iterazione:

int main() { 
    int counter = 0; 
    while (counter < 1000) { 
     Class myObject; 
     // stuff happens, each iteration has a brand new object 
    } 
} 

Nota che Class MyClass(); fa non definire un oggetto di tipo Class, denominata MyClass, e costruire la macchina senza parametri. Dichiara una funzione chiamata MyClass, che non accetta parametri e che restituisce un oggetto di tipo Class. Presumibilmente nel tuo codice reale, il costruttore ha uno o più parametri.

+0

Io uso i costruttori, ma raramente passo i parametri, anche se so che posso farlo. Il più delle volte mi sento come se dovessi metterli all'interno del costruttore nel codice sorgente. Perché passare i parametri per farlo tramite main? – Azoreo

+0

In tal caso sono molto sorpreso dalla compilazione del codice, se non si utilizzano i parametri del costruttore ma anche non si utilizza la sintassi corretta per i costruttori senza parametri. Ad ogni modo, la ragione per usare i parametri del costruttore è se gli oggetti della tua classe non sono tutti uguali. Dai un'occhiata alla libreria standard per gli esempi: puoi specificare la dimensione iniziale e il contenuto di un vettore e così via. –

+0

Utilizzo di Microsoft Visual Studio C++ e del suo compilatore incorporato ... Se dichiaro un costruttore come KnightsTour :: KnightsTour (ad esempio) e quindi inserire KnightsTour; nel mio file di intestazione, genera errori, dicendo che sembra una funzione, ma che non ci sono parametri. Soooo ... Ho aggiunto una lista di parametri vuota, ad esempio "KnightsTour :: KnightsTour()" e quindi "KnightsTour();" nel file header - compila e corre! – Azoreo

1

Sì, questo uso non tipico. Crea una funzione che reimposta le variabili e chiama il metodo ogni volta che ne hai bisogno.

0

Sei caduto in preda a una lettura errata comune di C++. Il nuovo C++ 0x rende le cose un po 'più chiare.

Il problema è la sintassi delle costruzioni simile a una chiamata di funzione.

void foo(int i) { } 
class Foo { }; 

Foo(10); // construct a temporary object of type foo 
foo(10); // call function foo 
Foo{10}; // construct a temporary object of type foo in c++0x syntax 

Penso che la sintassi del C++ 0x sia più chiara.

Si potrebbe fare ciò che si desidera con questa sintassi. Ma attenzione è molto avanzato e si dovrebbe non farlo.

MyClass.~Class(); // destruct MyClass 
new(&MyClass) Class; 
+0

La principale ragione pratica per non farlo è che se il costruttore di 'Class' genera, allora l'oggetto' MyClass' si trova in uno stato incoerente. Lo srotolamento dello stack tenterà di distruggerlo nuovamente, producendo un comportamento indefinito. Questo, e non ti guadagna nulla su una funzione membro 'operator =' o 'reset' ben scritta. Quindi se controlli 'MyClass' non c'è bisogno di farlo, e se non controlli' MyClass', allora sta chiedendo dei problemi ;-) –

5

Che cosa succede in quella lettura linea ...

Class(); 

è che in realtà chiamare il costruttore - per un oggetto temporaneo che si sta costruendo da zero, e che viene poi immediatamente distrutta dal momento che non stai facendo nulla con esso. È molto simile al casting in Class, che crea un valore utilizzando una chiamata del costruttore, tranne che in questo caso non c'è alcun valore da trasmettere in modo da utilizzare il costruttore predefinito.

È possibile che il compilatore quindi ottimizzi questo provvisorio, quindi non c'è alcun costruttore - non sono sicuro che sia permesso o meno.

Se si desidera reinizializzare i membri, chiamare il costruttore non è il modo per farlo. Spostare tutto il codice di inizializzazione in un altro metodo e chiamarlo dal costruttore e quando si desidera reinizializzare, invece.

+2

"Non sono sicuro che sia permesso o meno" - lo è, ma solo se né il costruttore né il distruttore hanno alcun effetto sul comportamento osservabile del programma. Quindi se metti qualche traccia per vedere se sono omessi, non possono essere omessi. –

0

Con tali requisiti, in genere scrivo un metodo clear() (pubblico). Lo chiamo da costruttore, distruttore. Il codice utente può chiamarlo ogni volta che lo desidera.

class Foo 
{ 
    public: 

    Foo() { clear(); } 

    ~Foo() { clear(); } 

    void clear(); // (re)initialize the private members 

    private: 

    // private members 
}; 

Per rispondere alla domanda qui, il metodo clear() può essere chiamato ogni volta che è necessario per reinizializzare la classe come era appena dopo la costruzione iniziale.