2010-05-11 4 views
32

cosiderazione il seguente codice:C++ Matrice di puntatori: cancella o cancella []?

class Foo 
{ 
    Monster* monsters[6]; 

    Foo() 
    { 
     for (int i = 0; i < 6; i++) 
     { 
      monsters[i] = new Monster(); 
     } 
    } 

    virtual ~Foo(); 
} 

Qual è il distruttore corretta?

questo:

Foo::~Foo() 
{ 
    delete [] monsters; 
} 

o questo:

Foo::~Foo() 
{ 
    for (int i = 0; i < 6; i++) 
    { 
     delete monsters[i]; 
    } 
} 

Al momento ho il costruttore più in alto e tutto funziona okey, ma naturalmente non riesco a vedere se capita di essere che perde ...

Personalmente, penso che la seconda versione sia molto più logica considerando quello che sto facendo. Ad ogni modo, qual è il modo "corretto" per farlo?

risposta

36

delete[] monsters;

è corretto perché monsters non è un puntatore a un array allocato dinamicamente, esso è un array di puntatori. Come membro della classe, verrà distrutto automaticamente quando l'istanza della classe viene distrutta.

L'altra implementazione è quella corretta in quanto i puntatori nell'array fanno riferimento agli oggetti dinamici assegnati Monster.

Si noti che con la strategia di allocazione della memoria corrente si desidera dichiarare il proprio costruttore di copia e l'operatore di assegnazione della copia in modo che la copia non intenzionale non causi doppie eliminazioni. (Se si desidera impedire la copia, è possibile dichiararli come privati ​​e non implementarli effettivamente.)

+0

Puoi spiegare un po 'perché dovresti disabilitare il costruttore di copie e l'operatore di assegnazione delle copie? – gen

+0

@gen Questa è un'ipotesi, quindi prendila per quello che vale, ma sto * pensando * che senza l'assegnazione/costruzione di copia personalizzata può accadere quanto segue: 2 oggetti Foo con una serie di puntatori mostruosa che puntano al * stessi * mostri. Se distruggi Foo1, i puntatori di Foo2 non saranno validi poiché la memoria a cui puntano è stata liberata da Foo1. – ZeroStatic

2

Il secondo esempio è corretto; non è necessario eliminare lo stesso array monsters, ma solo i singoli oggetti creati.

31

Per new è necessario utilizzare delete. Per new[] utilizzare delete[]. La tua seconda variante è corretta.

11

Il secondo è corretto date le circostanze (beh, il meno sbagliato, comunque).

Edit: "meno male", come nel codice originale mostra alcuna buona ragione per essere utilizzando new o delete, in primo luogo, così si dovrebbe probabilmente basta usare:

std::vector<Monster> monsters; 

Il risultato sarà più semplice codice e separazione più chiara delle responsabilità.

+1

meno male? Si prega di elaborare. – Jasper

+0

@Jerry Suppongo che 'Monster' sia un tipo polimorfico. In tal caso, una sorta di puntatore deve essere usato come tipo di elemento del 'vector' per abilitare il polimorfismo. – fredoverflow

+0

@FredOverflow: mentre è certamente possibile che possa avere a che fare con una gerarchia polimorfica, 1) non ha effettivamente mostrato ciò, e 2) un vettore andrà comunque bene se lo è. –

0

Si elimina ogni puntatore singolarmente e quindi si elimina l'intero array. Assicurati di aver definito un distruttore appropriato per le classi che vengono archiviate nell'array, altrimenti non puoi essere sicuro che gli oggetti vengano ripuliti correttamente. Assicurati che tutti i tuoi distruttori siano virtuali in modo che si comportino correttamente quando usati con l'ereditarietà.

+0

Penso che fare entrambe le cose sarebbe piuttosto strano, dato che l'array ha una lunghezza costante, quindi non è necessario eliminarlo. Inoltre, ho dichiarato virtuale il distruttore, quindi il commento era abbastanza inutile. – Jasper

5

delete[] monsters è decisamente sbagliato. Il mio debugger mucchio mostra il seguente output:

allocated non-array memory at 0x3e38f0 (20 bytes) 
allocated non-array memory at 0x3e3920 (20 bytes) 
allocated non-array memory at 0x3e3950 (20 bytes) 
allocated non-array memory at 0x3e3980 (20 bytes) 
allocated non-array memory at 0x3e39b0 (20 bytes) 
allocated non-array memory at 0x3e39e0 (20 bytes) 
releasing  array memory at 0x22ff38 

Come si può vedere, si sta cercando di liberare con la forma sbagliata di cancellazione (non-array vs array), e il puntatore 0x22ff38 non è mai stato restituito da una chiama a nuovo.La seconda versione mostra l'uscita corretta:

[allocations omitted for brevity] 
releasing non-array memory at 0x3e38f0 
releasing non-array memory at 0x3e3920 
releasing non-array memory at 0x3e3950 
releasing non-array memory at 0x3e3980 
releasing non-array memory at 0x3e39b0 
releasing non-array memory at 0x3e39e0 

Comunque, preferisco un disegno dove attuare manualmente il distruttore non è necessario per cominciare.

#include <array> 
#include <memory> 

class Foo 
{ 
    std::array<std::shared_ptr<Monster>, 6> monsters; 

    Foo() 
    { 
     for (int i = 0; i < 6; ++i) 
     { 
      monsters[i].reset(new Monster()); 
     } 
    } 

    virtual ~Foo() 
    { 
     // nothing to do manually 
    } 
}; 
0

Sarebbe rendere sens se il codice è stato come questo:

#include <iostream> 

using namespace std; 

class Monster 
{ 
public: 
     Monster() { cout << "Monster!" << endl; } 
     virtual ~Monster() { cout << "Monster Died" << endl; } 
}; 

int main(int argc, const char* argv[]) 
{ 
     Monster *mon = new Monster[6]; 

     delete [] mon; 

     return 0; 
} 
10

Per semplificare l'Answare diamo un'occhiata alla seguente codice:

#include "stdafx.h" 
#include <iostream> 
using namespace std; 

class A 
{ 
private: 
    int m_id; 
    static int count; 
public: 
    A() {count++; m_id = count;} 
    A(int id) { m_id = id; } 
    ~A() {cout<< "Destructor A " <<m_id<<endl; } 
}; 

int A::count = 0; 

void f1() 
{ 
    A* arr = new A[10]; 
    //delete operate only one constructor, and crash! 
    delete arr; 
    //delete[] arr; 
} 

int main() 
{ 
    f1(); 
    system("PAUSE"); 
    return 0; 
} 

L'output è: Destructor A 1 e poi si blocca (Espressione: _BLOCK_TYPE_IS_VALID (phead-nBlockUse)).

Dobbiamo usare: delete [] arr; perché si cancella l'intero array e non solo una cella!

tenta di utilizzare delete [] arr; l'uscita è: distruttore A 10 distruttore A 9 distruttore A 8 distruttore A 7 distruttore A 6 distruttore A 5 distruttore A 4 distruttore A 3 distruttore A 2 distruttore A 1

Lo stesso principio è per una serie di puntatori:

void f2() 
{ 
    A** arr = new A*[10]; 
    for(int i = 0; i < 10; i++) 
    { 
     arr[i] = new A(i); 
    } 
    for(int i = 0; i < 10; i++) 
    { 
     delete arr[i];//delete the A object allocations. 
    } 

    delete[] arr;//delete the array of pointers 
} 

se si userà delete arr anziché delete [] arr. non cancellerà l'intero puntatore dell'array => perdita di memoria degli oggetti puntatore!