Domanda

Ogni volta che ho bisogno di aggiungere all'oggetto allocato in modo dinamico in un vettore che ho fatto che nel seguente modo:

class Foo { ... };

vector<Foo*> v;

v.push_back(new Foo);

// do stuff with Foo in v

// delete all Foo in v

E 'appena finito di lavorare e molti altri sembrano fare la stessa cosa.

Oggi, ho imparato vector :: push_back può generare un'eccezione. Ciò significa che il codice di cui sopra non è sicuro eccezione. :-( Così mi si avvicinò con una soluzione:

class Foo { ... };

vector<Foo*> v;
auto_ptr<Foo> p(new Foo);

v.push_back(p.get());
p.release();

// do stuff with Foo in v

// delete all Foo in v

Ma il problema è che il nuovo modo è prolisso, noioso, e vedo nessuno di farlo. (Almeno non intorno a me ...)

Devo andare con il nuovo modo?
O, posso solo rimanere con il vecchio modo?
Oppure, c'è un modo migliore di farlo?

È stato utile?

Soluzione

Se tutto ciò che interessa è eccezioni sicurezza di questa operazione:

v.reserve(v.size()+1);  // reserve can throw, but that doesn't matter
v.push_back(new Foo);   // new can throw, that doesn't matter either.

Il rilascio di un vettore che ha la responsabilità per liberare gli oggetti puntati da suo contenuto è una cosa a parte, sono sicuro che si otterrà un sacco di consigli su quello; -)

Modifica: hmm, stavo per citare lo standard, ma io in realtà non riesce a trovare la garanzia necessaria. Quello che sto cercando è che push_back non buttare a meno che (a) deve riallocare (che sappiamo che non sarà a causa della capacità), o (b) un costruttore di T getta (che sappiamo ha vinto 't poiché T è un tipo puntatore). Suoni ragionevole, ma ragionevole! = Garantite.

Quindi, a meno che non ci sia una risposta benefica sopra su questa domanda:

Is std :: vector :: push_back consentito di gettare per qualsiasi ragione diversa riallocazione fallito o di costruzione?

questo codice dipende dall'implementazione non fare nulla di troppo "fantasiosa". In mancanza di ciò, la soluzione della questione può essere templato up:

template <typename T, typename Container>
void push_back_new(Container &c) {
    auto_ptr<T> p(new T);
    c.push_back(p.get());
    p.release();
}

Utilizzo quindi non è troppo noioso:

struct Bar : Foo { };

vector<Foo*> v;
push_back_new<Foo>(v);
push_back_new<Bar>(v);

Se è davvero una funzione di fabbrica piuttosto che new allora si potrebbe modificare il modello di conseguenza. Passando un sacco di diverse liste di parametri in diverse situazioni sarebbe difficile, però.

Altri suggerimenti

Il nuovo modo è più eccezione sicura ma c'è una ragione che non si vederlo fatto in qualsiasi altro luogo.

Un vector di puntatori possiede solo i puntatori, non esprime la proprietà degli oggetti appuntiti-to. Si sta effettivamente rilasciando proprietà ad un oggetto che non "vuole" proprietà.

La maggior parte delle persone userà un vector di shared_ptr per esprimere la proprietà correttamente o usare qualcosa come boost::ptr_vector. Uno di questi significa che non c'è bisogno di delete esplicitamente gli oggetti di cui puntatori si archiviano, che è soggetto ad errori e potenzialmente eccezione 'pericoloso' in altri punti del programma.

Modifica Hai ancora essere molto attenti con l'inserimento in ptr_vector. Purtroppo, push_back prendendo un puntatore raw fornisce l'forte garanzia che significa che o inserimento riesce o (efficace) non accade, quindi l'oggetto passato è né ripreso né distrutta. La versione di prendere un puntatore intelligente per valore è definito come chiamare .release() prima di chiamare la versione fortemente garantito che di fatto significa che può fuoriuscire.

Utilizzando un vector di shared_ptr insieme make_shared è molto più facile da usare in modo corretto.

Il modo migliore per farlo è quello di utilizzare un contenitore di puntatori intelligenti, per esempio, un std::vector<std::shared_ptr<Foo> > o std::vector<std::unique_ptr<Foo> > (shared_ptr possono essere trovati in Boost e C ++ TR1 pure; std::unique_ptr è efficacemente limitato a C ++ 0x).

Un'altra opzione è quella di utilizzare un contenitore che possiede oggetti dinamici, come quei contenitori forniti dalla libreria Boost Pointer contenitori.

Come resistente alla carenza di memoria è il vostro programma? Se davvero a cuore questo si deve essere preparati per new buttare pure. Se non si ha intenzione di gestire questo, io non mi preoccuperei salto attraverso i cerchi push_back.

Nel caso generale, se si esaurisce la memoria, il programma ha già problemi probabilmente insormontabili meno che non sia specificamente progettato per funzionare per sempre in un ingombro vincolata (sistemi embedded) - in questo caso si ha a cura di tutti questi casi.

Se questo vale per voi, si potrebbe avere una revisione del codice lungo e ripetere il test in vista del ciclo di voi. La mia ipotesi è che si è OK per seguire la pratica della tua squadra qui, però.

Come altri hanno già fuori a punta, utilizzando vector per memorizzare i puntatori prime ha i suoi problemi, e non v'è una grande quantità di materiale su questo sito e nelle altre risposte di indirizzarvi verso un modello più sicuro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top