2012-10-05 2 views
6

Sto riscontrando un problema con un distruttore chiamato per una classe alla fine di una subroutine, anche se dovrebbe essere definito al di fuori dell'ambito della subroutine.C++: il distruttore viene chiamato prima che debba uscire dal campo di applicazione?

Ecco il pezzo più piccolo di codice che ho che visualizza il mio problema:

#include <iostream> 
using namespace std; 

class Foo { 
private: 

    double *array; 

public: 

Foo(int N) { 
    array = new double[N]; 
    for (int i=0; i<N; i++) { 
     array[i]=0; 
    } 
} 

~Foo() { 
    delete[] array; 
} 
}; 

void subroutine(Foo x) { 
    cout << "Hello!" << endl; 
} 

int main() { 
    Foo bar(10); 
    subroutine(bar); 
    subroutine(bar); 
} 

Ora il distruttore per la barra degli oggetti qui viene chiamato dopo la prima subroutine termina anche se è portata dovrebbe essere tutta la funzione principale? Ciò significa che quando chiamo la seconda subroutine il distruttore viene chiamato di nuovo e ottengo una perdita di memoria.

Ho trovato che posso risolvere questo problema chiamando per riferimento nella subroutine ma non sono molto soddisfatto di questa correzione poiché non capisco perché non ha funzionato in primo luogo. Qualcuno può far luce su questo per me?

Grazie.

+2

Dato il distruttore, è necessario definire o eliminare il costruttore di copia di 'Foo' e l'operatore di assegnazione copia. Cerca "regola dei tre". –

+5

"Il distruttore viene chiamato per una classe" - scoprirai che, nel tempo, le cose sono molto più chiare se distingui costantemente tra ** classe ** e ** oggetto **. I distruttori sono chiamati su ** oggetti ** non sulle classi. –

risposta

21

Si sta passando un valore Foo alla funzione subroutine. Ciò significa che ha la sua copia, che viene distrutta all'uscita dal suo scopo.

void subroutine(Foo x) { 
    // x is a new Foo here. It will get destroyed on exiting scope, 
    // resulting in a destructor call 
} 

Il tuo problema principale qui è che non è stato implementato un costruttore di copia, quindi l'array allocato dinamicamente non viene copiato (solo il puntatore che punta ad essa è). Pertanto, quando si copiano gli oggetti , ogni copia si riferisce allo stesso array. E ogni copia cercherà di distruggerlo.

Si dovrebbe seguire il rule of three e implementare un operatore di assegnazione e un costruttore di copia che fare una "copia completa" della matrice, in modo tale che ogni oggetto Foo possiede la sua propria matrice.

+0

Ah grazie per la rapida risposta! Sì, avevo notato che chiamare per riferimento l'aveva risolto ma non sapevo perché, grazie per la spiegazione! – Plog

+1

@ user1722882: In realtà, sarebbe meglio usare un 'std :: vector' e rimuovere completamente il distruttore. Reimplementare i contenitori della Biblioteca standard potrebbe essere utile per il suo sport, ma quando non è lo scopo principale dell'esercizio/lavoro ... sta solo uccidendo la tua produttività. –

1

Il problema che stai avendo è che si sta passando l'oggetto per valore:

void subroutine(Foo x) { 

Questo sta creando un oggetto temporaneo e invocando il costruttore di copia/distruttore del vostro oggetto ogni volta che si chiama.

3

Quando si chiama void subroutine(Foo x) { l'oggetto bar viene copiato (quindi il distruttore viene chiamato dopo il termine della funzione).

Provare a utilizzare: void subroutine(Foo &x) {, dovrebbe funzionare bene.

6

Si sta passando una barra per valore alla subroutine in modo da creare una copia. Per evitare di fare una copia passare per riferimento:

void subroutine(Foo& x) 
{ 
    cout << "Hello!" << endl; 
} 

È possibile evitare che le copie accidentali della vostra classe dichiarando il costruttore di copia e copiare l'assegnazione operatore privato in questo modo:

class Foo { 
private: 

    double *array; 

    Foo(const Foo&); 
    Foo& operator=(const foo&); 

public: 
    ... 
}; 

quindi si ottiene una compilation errore invece. Se hai davvero bisogno di essere in grado di fare una copia della tua classe allora dovrai effettivamente implementare queste funzioni per eseguire una "copia profonda" (o meglio ancora usare std::vector<float> e lasciare che gestisca la memoria per te inclusa la copia sicura).

+0

con C++ 11 è meglio dichiararli come 'deleted' invece –