Perché viene chiamato il distruttore della mia class quando aggiungo istanze a un vettore?

Sembra che ogni volta che aggiungo un object al vettore m_test, venga chiamato il metodo del distruttore. Mi sto perdendo qualcosa? Come posso evitare che ciò accada?

class TEST { public: TEST(); ~TEST(); int * x; }; TEST::TEST() { } TEST::~TEST() { ... it is called every time I push_back something to the vector ... delete x; } vector m_test; for (unsigned int i=0; i<5; i++) { m_test.push_back(TEST()); } 

Il problema qui è che stai violando la Regola del Tre . La tua class ha un distruttore quindi hai bisogno anche di un costruttore di copia e di un operatore di assegnazione. In alternativa, non puoi permettere che la tua class sia copiata (per esempio facendo T(T const&) e T& operator=(T const&) private, o derivando da boost::noncopyable ), e poi ridimensiona il vettore invece di usare push_back .

Nel primo caso, puoi semplicemente push_back il push_back tua class come faresti normalmente. Nel secondo, la syntax sarebbe qualcosa di simile

 std::vector vec(5); // vec now has five default-constructed elements of type TEST. 

Non fare nessuna di queste cose è una ctriggers idea, dato che a un certo punto è molto probabile che si verifichino problemi di doppia eliminazione – anche se pensi che non copierai o assegnerai mai un TEST dove x != nullptr , è molto più sicuro vietarlo esplicitamente.

A proposito, se hai dei puntatori membri che dovrebbero essere cancellati quando un object esce dall’ambito, scoped_ptr considerazione l’uso di puntatori intelligenti come scoped_ptr , unique_ptr e shared_ptr (e forse auto_ptr se non riesci a usare Boost o C ++ 11).

Non viene chiamato quando si push_back , viene chiamato quando il temporaneo viene distrutto .

Per risolvere il problema nel tuo esempio:

 TEST test; for (int i = 0; i < 5; ++i) { m_test.push_back(test); } 

Dovrebbe solo chiamarlo una volta.

Il tuo codice sta creando un TEST temporaneo all'interno del ciclo, usandolo in push_back , quindi quel temporaneo sta uscendo dall'ambito quando il ciclo termina / si ripete e viene distrutto. Ciò avviene esattamente come dovrebbe, dal momento che il TEST temporaneo deve essere ripulito.

Se vuoi evitarlo, devi fare qualsiasi altra cosa, ma creare un object temporaneo per ogni spinta. Una ansible soluzione è:

 vector m_test(5); // Note reserving space in the vector for 5 objects std::fill(m_test.begin(), m_test.end(), TEST()); // Fill the vector with the default ctor 

A seconda di come il tuo STL è ottimizzato, potrebbe non essere necessario eseguire più copie.

Potresti anche essere in grado di ottenere una migliore gestione se implementi un costruttore di copie nella tua class TEST , ad esempio:

 TEST::TEST(const TEST & other) { x = new int(*other.x); // Not entirely safe, but the simplest copy ctor for this example. } 

Se questo è appropriato o come lo gestisci, dipende dalla tua class e dalle sue esigenze, ma in genere dovresti avere un costruttore di copie quando hai definito il tuo costruttore e distruttore regolare (altrimenti il ​​compilatore ne genererà uno, e in questo caso, risulterà in puntatori copiati e appesi a x ).

vector.push_back() copia l’object dato nella sua area di memorizzazione. L’object temporaneo che stai costruendo nella chiamata push_back() viene distrutto immediatamente dopo essere stato copiato, e questo è ciò che stai vedendo. Alcuni compilatori potrebbero essere in grado di ottimizzare questa copia, ma a quanto pare non è ansible.

In m_test.push_back(TEST()); , TEST () creerà una variabile temporanea. Dopo che il vettore lo ha copiato nella sua memoria, la variabile temporanea viene distrutta.

Puoi fare così:

 vector m_test(5, TEST()); 

Per evitare la distruzione di un temporaneo e per evitare i costruttori di copia, considerare l’uso di vector :: resize o vector :: emplace_back . Ecco un esempio utilizzando emplace_back :

 vector m_test; m_test.reserve(5); for ( uint i=0; i<5; i++ ) { m_test.emplace_back(); } 

L'elemento vettoriale sarà costruito sul posto senza necessità di copiare. Quando vt viene distrutto, ogni elemento vettoriale viene automaticamente distrutto.

c ++ 0x è richiesto (usa -std=c++0x con gnu). #include è ovviamente richiesto.

Se non viene utilizzato un costruttore predefinito (ad esempio, se TEST::x era un riferimento anziché un puntatore), è sufficiente aggiungere argomentazioni alla chiamata a emplace_back() come segue:

 class TEST { public: TEST( int & arg) : x(arg) {;} // no default constructor int & x; // reference instead of a pointer. }; . . . int someInt; vector m_test; m_test.reserve(5); for ( uint i=0; i<5; i++ ) { m_test.emplace_back( someInt ); // TEST constructor args added here. } 

La reserve() mostrata è facoltativa ma assicura che sia disponibile spazio sufficiente prima di iniziare a build elementi vettoriali.